/[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 3118 - (show annotations) (download)
Fri Apr 21 13:33:03 2017 UTC (2 months ago) by schoenebeck
File size: 53222 byte(s)
* NKSP: Fixed crash when using built-in script array variable "%ALL_EVENTS".
* NKSP: Added built-in function "change_amp_lfo_depth()".
* NKSP: Added built-in function "change_amp_lfo_freq()".
* NKSP: Added built-in function "change_pitch_lfo_depth()".
* NKSP: Added built-in function "change_pitch_lfo_freq()".
* Bumped version (2.0.0.svn44).

1 /*
2 * Copyright (c) 2014-2017 Christian Schoenebeck
3 *
4 * http://www.linuxsampler.org
5 *
6 * This file is part of LinuxSampler and released under the same terms.
7 * See README file for details.
8 */
9
10 #include "InstrumentScriptVMFunctions.h"
11 #include "InstrumentScriptVM.h"
12 #include "../AbstractEngineChannel.h"
13
14 namespace LinuxSampler {
15
16 // play_note() function
17
18 InstrumentScriptVMFunction_play_note::InstrumentScriptVMFunction_play_note(InstrumentScriptVM* parent)
19 : m_vm(parent)
20 {
21 }
22
23 VMFnResult* InstrumentScriptVMFunction_play_note::exec(VMFnArgs* args) {
24 int note = args->arg(0)->asInt()->evalInt();
25 int velocity = (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : 127;
26 int sampleoffset = (args->argsCount() >= 3) ? args->arg(2)->asInt()->evalInt() : 0;
27 int duration = (args->argsCount() >= 4) ? args->arg(3)->asInt()->evalInt() : 0; //TODO: -1 might be a better default value instead of 0
28
29 if (note < 0 || note > 127) {
30 errMsg("play_note(): argument 1 is an invalid note number");
31 return errorResult(0);
32 }
33
34 if (velocity < 0 || velocity > 127) {
35 errMsg("play_note(): argument 2 is an invalid velocity value");
36 return errorResult(0);
37 }
38
39 if (sampleoffset < 0) {
40 errMsg("play_note(): argument 3 may not be a negative sample offset");
41 return errorResult(0);
42 } else if (sampleoffset != 0) {
43 wrnMsg("play_note(): argument 3 does not support a sample offset other than 0 yet");
44 }
45
46 if (duration < -1) {
47 errMsg("play_note(): argument 4 must be a duration value of at least -1 or higher");
48 return errorResult(0);
49 }
50
51 AbstractEngineChannel* pEngineChannel =
52 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
53
54 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
55 e.Init(); // clear IDs
56 e.Type = Event::type_play_note;
57 e.Param.Note.Key = note;
58 e.Param.Note.Velocity = velocity;
59 // make this new note dependent to the life time of the original note
60 if (duration == -1) {
61 if (m_vm->currentVMEventHandler()->eventHandlerType() != VM_EVENT_HANDLER_NOTE) {
62 errMsg("play_note(): -1 for argument 4 may only be used for note event handlers");
63 return errorResult(0);
64 }
65 e.Param.Note.ParentNoteID = m_vm->m_event->cause.Param.Note.ID;
66 }
67
68 const note_id_t id = pEngineChannel->ScheduleNoteMicroSec(&e, 0);
69
70 // if a duration is supplied (and play-note event was scheduled
71 // successfully above), then schedule a subsequent stop-note event
72 if (id && duration > 0) {
73 e.Type = Event::type_stop_note;
74 e.Param.Note.ID = id;
75 e.Param.Note.Velocity = 127;
76 pEngineChannel->ScheduleEventMicroSec(&e, duration);
77 }
78
79 // even if id is null, don't return an errorResult() here, because that
80 // would abort the script, and under heavy load it may be considerable
81 // that ScheduleNoteMicroSec() fails above, so simply ignore that
82 return successResult( ScriptID::fromNoteID(id) );
83 }
84
85 // set_controller() function
86
87 InstrumentScriptVMFunction_set_controller::InstrumentScriptVMFunction_set_controller(InstrumentScriptVM* parent)
88 : m_vm(parent)
89 {
90 }
91
92 VMFnResult* InstrumentScriptVMFunction_set_controller::exec(VMFnArgs* args) {
93 int controller = args->arg(0)->asInt()->evalInt();
94 int value = args->arg(1)->asInt()->evalInt();
95
96 AbstractEngineChannel* pEngineChannel =
97 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
98
99 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
100 e.Init(); // clear IDs
101 if (controller == CTRL_TABLE_IDX_AFTERTOUCH) {
102 e.Type = Event::type_channel_pressure;
103 e.Param.ChannelPressure.Value = value & 127;
104 } else if (controller == CTRL_TABLE_IDX_PITCHBEND) {
105 e.Type = Event::type_pitchbend;
106 e.Param.Pitch.Pitch = value;
107 } else if (controller >= 0 && controller <= 127) {
108 e.Type = Event::type_control_change;
109 e.Param.CC.Controller = controller;
110 e.Param.CC.Value = value;
111 } else {
112 errMsg("set_controller(): argument 1 is an invalid controller");
113 return errorResult();
114 }
115
116 const event_id_t id = pEngineChannel->ScheduleEventMicroSec(&e, 0);
117
118 // even if id is null, don't return an errorResult() here, because that
119 // would abort the script, and under heavy load it may be considerable
120 // that ScheduleEventMicroSec() fails above, so simply ignore that
121 return successResult( ScriptID::fromEventID(id) );
122 }
123
124 // ignore_event() function
125
126 InstrumentScriptVMFunction_ignore_event::InstrumentScriptVMFunction_ignore_event(InstrumentScriptVM* parent)
127 : m_vm(parent)
128 {
129 }
130
131 bool InstrumentScriptVMFunction_ignore_event::acceptsArgType(int iArg, ExprType_t type) const {
132 return type == INT_EXPR || type == INT_ARR_EXPR;
133 }
134
135 VMFnResult* InstrumentScriptVMFunction_ignore_event::exec(VMFnArgs* args) {
136 AbstractEngineChannel* pEngineChannel =
137 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
138
139 if (args->arg(0)->exprType() == INT_EXPR) {
140 const ScriptID id = args->arg(0)->asInt()->evalInt();
141 if (!id) {
142 wrnMsg("ignore_event(): event ID argument may not be zero");
143 // not errorResult(), because that would abort the script, not intentional in this case
144 return successResult();
145 }
146 pEngineChannel->IgnoreEventByScriptID(id);
147 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
148 VMIntArrayExpr* ids = args->arg(0)->asIntArray();
149 for (int i = 0; i < ids->arraySize(); ++i) {
150 const ScriptID id = ids->evalIntElement(i);
151 pEngineChannel->IgnoreEventByScriptID(id);
152 }
153 }
154
155 return successResult();
156 }
157
158 // ignore_controller() function
159
160 InstrumentScriptVMFunction_ignore_controller::InstrumentScriptVMFunction_ignore_controller(InstrumentScriptVM* parent)
161 : m_vm(parent)
162 {
163 }
164
165 VMFnResult* InstrumentScriptVMFunction_ignore_controller::exec(VMFnArgs* args) {
166 const ScriptID id = (args->argsCount() >= 1) ? args->arg(0)->asInt()->evalInt() : m_vm->m_event->id;
167 if (!id && args->argsCount() >= 1) {
168 wrnMsg("ignore_controller(): event ID argument may not be zero");
169 return successResult();
170 }
171
172 AbstractEngineChannel* pEngineChannel =
173 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
174
175 pEngineChannel->IgnoreEventByScriptID(id);
176
177 return successResult();
178 }
179
180 // note_off() function
181
182 InstrumentScriptVMFunction_note_off::InstrumentScriptVMFunction_note_off(InstrumentScriptVM* parent)
183 : m_vm(parent)
184 {
185 }
186
187 bool InstrumentScriptVMFunction_note_off::acceptsArgType(int iArg, ExprType_t type) const {
188 return type == INT_EXPR || type == INT_ARR_EXPR;
189 }
190
191 VMFnResult* InstrumentScriptVMFunction_note_off::exec(VMFnArgs* args) {
192 AbstractEngineChannel* pEngineChannel =
193 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
194
195 int velocity = (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : 127;
196 if (velocity < 0 || velocity > 127) {
197 errMsg("note_off(): argument 2 is an invalid velocity value");
198 return errorResult();
199 }
200
201 if (args->arg(0)->exprType() == INT_EXPR) {
202 const ScriptID id = args->arg(0)->asInt()->evalInt();
203 if (!id) {
204 wrnMsg("note_off(): note ID for argument 1 may not be zero");
205 return successResult();
206 }
207 if (!id.isNoteID()) {
208 wrnMsg("note_off(): argument 1 is not a note ID");
209 return successResult();
210 }
211
212 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
213 if (!pNote) return successResult();
214
215 Event e = pNote->cause;
216 e.Init(); // clear IDs
217 e.CopyTimeFrom(m_vm->m_event->cause); // set fragment time for "now"
218 e.Type = Event::type_stop_note;
219 e.Param.Note.ID = id.noteID();
220 e.Param.Note.Key = pNote->hostKey;
221 e.Param.Note.Velocity = velocity;
222
223 pEngineChannel->ScheduleEventMicroSec(&e, 0);
224 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
225 VMIntArrayExpr* ids = args->arg(0)->asIntArray();
226 for (int i = 0; i < ids->arraySize(); ++i) {
227 const ScriptID id = ids->evalIntElement(i);
228 if (!id || !id.isNoteID()) continue;
229
230 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
231 if (!pNote) continue;
232
233 Event e = pNote->cause;
234 e.Init(); // clear IDs
235 e.CopyTimeFrom(m_vm->m_event->cause); // set fragment time for "now"
236 e.Type = Event::type_stop_note;
237 e.Param.Note.ID = id.noteID();
238 e.Param.Note.Key = pNote->hostKey;
239 e.Param.Note.Velocity = velocity;
240
241 pEngineChannel->ScheduleEventMicroSec(&e, 0);
242 }
243 }
244
245 return successResult();
246 }
247
248 // set_event_mark() function
249
250 InstrumentScriptVMFunction_set_event_mark::InstrumentScriptVMFunction_set_event_mark(InstrumentScriptVM* parent)
251 : m_vm(parent)
252 {
253 }
254
255 VMFnResult* InstrumentScriptVMFunction_set_event_mark::exec(VMFnArgs* args) {
256 const ScriptID id = args->arg(0)->asInt()->evalInt();
257 const int groupID = args->arg(1)->asInt()->evalInt();
258
259 if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) {
260 errMsg("set_event_mark(): argument 2 is an invalid group id");
261 return errorResult();
262 }
263
264 AbstractEngineChannel* pEngineChannel =
265 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
266
267 // check if the event/note still exists
268 switch (id.type()) {
269 case ScriptID::EVENT: {
270 RTList<Event>::Iterator itEvent = pEngineChannel->pEngine->EventByID( id.eventID() );
271 if (!itEvent) return successResult();
272 break;
273 }
274 case ScriptID::NOTE: {
275 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
276 if (!pNote) return successResult();
277 break;
278 }
279 }
280
281 pEngineChannel->pScript->eventGroups[groupID].insert(id);
282
283 return successResult();
284 }
285
286 // delete_event_mark() function
287
288 InstrumentScriptVMFunction_delete_event_mark::InstrumentScriptVMFunction_delete_event_mark(InstrumentScriptVM* parent)
289 : m_vm(parent)
290 {
291 }
292
293 VMFnResult* InstrumentScriptVMFunction_delete_event_mark::exec(VMFnArgs* args) {
294 const ScriptID id = args->arg(0)->asInt()->evalInt();
295 const int groupID = args->arg(1)->asInt()->evalInt();
296
297 if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) {
298 errMsg("delete_event_mark(): argument 2 is an invalid group id");
299 return errorResult();
300 }
301
302 AbstractEngineChannel* pEngineChannel =
303 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
304
305 pEngineChannel->pScript->eventGroups[groupID].erase(id);
306
307 return successResult();
308 }
309
310 // by_marks() function
311
312 InstrumentScriptVMFunction_by_marks::InstrumentScriptVMFunction_by_marks(InstrumentScriptVM* parent)
313 : m_vm(parent)
314 {
315 }
316
317 int InstrumentScriptVMFunction_by_marks::Result::arraySize() const {
318 return eventGroup->size();
319 }
320
321 int InstrumentScriptVMFunction_by_marks::Result::evalIntElement(uint i) {
322 return (*eventGroup)[i];
323 }
324
325 VMFnResult* InstrumentScriptVMFunction_by_marks::errorResult() {
326 m_result.eventGroup = NULL;
327 m_result.flags = StmtFlags_t(STMT_ABORT_SIGNALLED | STMT_ERROR_OCCURRED);
328 return &m_result;
329 }
330
331 VMFnResult* InstrumentScriptVMFunction_by_marks::successResult(EventGroup* eventGroup) {
332 m_result.eventGroup = eventGroup;
333 m_result.flags = STMT_SUCCESS;
334 return &m_result;
335 }
336
337 VMFnResult* InstrumentScriptVMFunction_by_marks::exec(VMFnArgs* args) {
338 int groupID = args->arg(0)->asInt()->evalInt();
339
340 if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) {
341 errMsg("by_marks(): argument is an invalid group id");
342 return errorResult();
343 }
344
345 AbstractEngineChannel* pEngineChannel =
346 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
347
348 return successResult( &pEngineChannel->pScript->eventGroups[groupID] );
349 }
350
351 // change_vol() function
352
353 InstrumentScriptVMFunction_change_vol::InstrumentScriptVMFunction_change_vol(InstrumentScriptVM* parent)
354 : m_vm(parent)
355 {
356 }
357
358 bool InstrumentScriptVMFunction_change_vol::acceptsArgType(int iArg, ExprType_t type) const {
359 if (iArg == 0)
360 return type == INT_EXPR || type == INT_ARR_EXPR;
361 else
362 return INT_EXPR;
363 }
364
365 VMFnResult* InstrumentScriptVMFunction_change_vol::exec(VMFnArgs* args) {
366 int volume = args->arg(1)->asInt()->evalInt(); // volume change in milli dB
367 bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;
368 const float fVolumeLin = RTMath::DecibelToLinRatio(float(volume) / 1000.f);
369
370 AbstractEngineChannel* pEngineChannel =
371 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
372
373 if (args->arg(0)->exprType() == INT_EXPR) {
374 const ScriptID id = args->arg(0)->asInt()->evalInt();
375 if (!id) {
376 wrnMsg("change_vol(): note ID for argument 1 may not be zero");
377 return successResult();
378 }
379 if (!id.isNoteID()) {
380 wrnMsg("change_vol(): argument 1 is not a note ID");
381 return successResult();
382 }
383
384 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
385 if (!pNote) return successResult();
386
387 // if change_vol() was called immediately after note was triggered
388 // then immediately apply the volume to note object
389 if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
390 if (relative)
391 pNote->Override.Volume *= fVolumeLin;
392 else
393 pNote->Override.Volume = fVolumeLin;
394 } else { // otherwise schedule the volume change ...
395 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
396 e.Init(); // clear IDs
397 e.Type = Event::type_note_synth_param;
398 e.Param.NoteSynthParam.NoteID = id.noteID();
399 e.Param.NoteSynthParam.Type = Event::synth_param_volume;
400 e.Param.NoteSynthParam.Delta = fVolumeLin;
401 e.Param.NoteSynthParam.Relative = relative;
402
403 pEngineChannel->ScheduleEventMicroSec(&e, 0);
404 }
405 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
406 VMIntArrayExpr* ids = args->arg(0)->asIntArray();
407 for (int i = 0; i < ids->arraySize(); ++i) {
408 const ScriptID id = ids->evalIntElement(i);
409 if (!id || !id.isNoteID()) continue;
410
411 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
412 if (!pNote) continue;
413
414 // if change_vol() was called immediately after note was triggered
415 // then immediately apply the volume to Note object
416 if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
417 if (relative)
418 pNote->Override.Volume *= fVolumeLin;
419 else
420 pNote->Override.Volume = fVolumeLin;
421 } else { // otherwise schedule the volume change ...
422 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
423 e.Init(); // clear IDs
424 e.Type = Event::type_note_synth_param;
425 e.Param.NoteSynthParam.NoteID = id.noteID();
426 e.Param.NoteSynthParam.Type = Event::synth_param_volume;
427 e.Param.NoteSynthParam.Delta = fVolumeLin;
428 e.Param.NoteSynthParam.Relative = relative;
429
430 pEngineChannel->ScheduleEventMicroSec(&e, 0);
431 }
432 }
433 }
434
435 return successResult();
436 }
437
438 // change_tune() function
439
440 InstrumentScriptVMFunction_change_tune::InstrumentScriptVMFunction_change_tune(InstrumentScriptVM* parent)
441 : m_vm(parent)
442 {
443 }
444
445 bool InstrumentScriptVMFunction_change_tune::acceptsArgType(int iArg, ExprType_t type) const {
446 if (iArg == 0)
447 return type == INT_EXPR || type == INT_ARR_EXPR;
448 else
449 return INT_EXPR;
450 }
451
452 VMFnResult* InstrumentScriptVMFunction_change_tune::exec(VMFnArgs* args) {
453 int tune = args->arg(1)->asInt()->evalInt(); // tuning change in milli cents
454 bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;
455 const float fFreqRatio = RTMath::CentsToFreqRatioUnlimited(float(tune) / 1000.f);
456
457 AbstractEngineChannel* pEngineChannel =
458 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
459
460 if (args->arg(0)->exprType() == INT_EXPR) {
461 const ScriptID id = args->arg(0)->asInt()->evalInt();
462 if (!id) {
463 wrnMsg("change_tune(): note ID for argument 1 may not be zero");
464 return successResult();
465 }
466 if (!id.isNoteID()) {
467 wrnMsg("change_tune(): argument 1 is not a note ID");
468 return successResult();
469 }
470
471 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
472 if (!pNote) return successResult();
473
474 // if change_tune() was called immediately after note was triggered
475 // then immediately apply the tuning to Note object
476 if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
477 if (relative)
478 pNote->Override.Pitch *= fFreqRatio;
479 else
480 pNote->Override.Pitch = fFreqRatio;
481 } else { // otherwise schedule tuning change ...
482 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
483 e.Init(); // clear IDs
484 e.Type = Event::type_note_synth_param;
485 e.Param.NoteSynthParam.NoteID = id.noteID();
486 e.Param.NoteSynthParam.Type = Event::synth_param_pitch;
487 e.Param.NoteSynthParam.Delta = fFreqRatio;
488 e.Param.NoteSynthParam.Relative = relative;
489
490 pEngineChannel->ScheduleEventMicroSec(&e, 0);
491 }
492 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
493 VMIntArrayExpr* ids = args->arg(0)->asIntArray();
494 for (int i = 0; i < ids->arraySize(); ++i) {
495 const ScriptID id = ids->evalIntElement(i);
496 if (!id || !id.isNoteID()) continue;
497
498 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
499 if (!pNote) continue;
500
501 // if change_tune() was called immediately after note was triggered
502 // then immediately apply the tuning to Note object
503 if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
504 if (relative)
505 pNote->Override.Pitch *= fFreqRatio;
506 else
507 pNote->Override.Pitch = fFreqRatio;
508 } else { // otherwise schedule tuning change ...
509 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
510 e.Init(); // clear IDs
511 e.Type = Event::type_note_synth_param;
512 e.Param.NoteSynthParam.NoteID = id.noteID();
513 e.Param.NoteSynthParam.Type = Event::synth_param_pitch;
514 e.Param.NoteSynthParam.Delta = fFreqRatio;
515 e.Param.NoteSynthParam.Relative = relative;
516
517 pEngineChannel->ScheduleEventMicroSec(&e, 0);
518 }
519 }
520 }
521
522 return successResult();
523 }
524
525 // change_pan() function
526
527 InstrumentScriptVMFunction_change_pan::InstrumentScriptVMFunction_change_pan(InstrumentScriptVM* parent)
528 : m_vm(parent)
529 {
530 }
531
532 bool InstrumentScriptVMFunction_change_pan::acceptsArgType(int iArg, ExprType_t type) const {
533 if (iArg == 0)
534 return type == INT_EXPR || type == INT_ARR_EXPR;
535 else
536 return INT_EXPR;
537 }
538
539 VMFnResult* InstrumentScriptVMFunction_change_pan::exec(VMFnArgs* args) {
540 int pan = args->arg(1)->asInt()->evalInt();
541 bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;
542
543 if (pan > 1000) {
544 wrnMsg("change_pan(): argument 2 may not be larger than 1000");
545 pan = 1000;
546 } else if (pan < -1000) {
547 wrnMsg("change_pan(): argument 2 may not be smaller than -1000");
548 pan = -1000;
549 }
550 const float fPan = float(pan) / 1000.f;
551
552 AbstractEngineChannel* pEngineChannel =
553 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
554
555 if (args->arg(0)->exprType() == INT_EXPR) {
556 const ScriptID id = args->arg(0)->asInt()->evalInt();
557 if (!id) {
558 wrnMsg("change_pan(): note ID for argument 1 may not be zero");
559 return successResult();
560 }
561 if (!id.isNoteID()) {
562 wrnMsg("change_pan(): argument 1 is not a note ID");
563 return successResult();
564 }
565
566 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
567 if (!pNote) return successResult();
568
569 // if change_pan() was called immediately after note was triggered
570 // then immediately apply the panning to Note object
571 if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
572 if (relative) {
573 pNote->Override.Pan = RTMath::RelativeSummedAvg(pNote->Override.Pan, fPan, ++pNote->Override.PanSources);
574 } else {
575 pNote->Override.Pan = fPan;
576 pNote->Override.PanSources = 1; // only relevant on subsequent change_pan() calls on same note with 'relative' being set
577 }
578 } else { // otherwise schedule panning change ...
579 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
580 e.Init(); // clear IDs
581 e.Type = Event::type_note_synth_param;
582 e.Param.NoteSynthParam.NoteID = id.noteID();
583 e.Param.NoteSynthParam.Type = Event::synth_param_pan;
584 e.Param.NoteSynthParam.Delta = fPan;
585 e.Param.NoteSynthParam.Relative = relative;
586
587 pEngineChannel->ScheduleEventMicroSec(&e, 0);
588 }
589 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
590 VMIntArrayExpr* ids = args->arg(0)->asIntArray();
591 for (int i = 0; i < ids->arraySize(); ++i) {
592 const ScriptID id = ids->evalIntElement(i);
593 if (!id || !id.isNoteID()) continue;
594
595 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
596 if (!pNote) continue;
597
598 // if change_pan() was called immediately after note was triggered
599 // then immediately apply the panning to Note object
600 if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
601 if (relative) {
602 pNote->Override.Pan = RTMath::RelativeSummedAvg(pNote->Override.Pan, fPan, ++pNote->Override.PanSources);
603 } else {
604 pNote->Override.Pan = fPan;
605 pNote->Override.PanSources = 1; // only relevant on subsequent change_pan() calls on same note with 'relative' being set
606 }
607 } else { // otherwise schedule panning change ...
608 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
609 e.Init(); // clear IDs
610 e.Type = Event::type_note_synth_param;
611 e.Param.NoteSynthParam.NoteID = id.noteID();
612 e.Param.NoteSynthParam.Type = Event::synth_param_pan;
613 e.Param.NoteSynthParam.Delta = fPan;
614 e.Param.NoteSynthParam.Relative = relative;
615
616 pEngineChannel->ScheduleEventMicroSec(&e, 0);
617 }
618 }
619 }
620
621 return successResult();
622 }
623
624 #define VM_FILTER_PAR_MAX_VALUE 1000000
625 #define VM_EG_PAR_MAX_VALUE 1000000
626
627 // change_cutoff() function
628
629 InstrumentScriptVMFunction_change_cutoff::InstrumentScriptVMFunction_change_cutoff(InstrumentScriptVM* parent)
630 : m_vm(parent)
631 {
632 }
633
634 bool InstrumentScriptVMFunction_change_cutoff::acceptsArgType(int iArg, ExprType_t type) const {
635 if (iArg == 0)
636 return type == INT_EXPR || type == INT_ARR_EXPR;
637 else
638 return INT_EXPR;
639 }
640
641 VMFnResult* InstrumentScriptVMFunction_change_cutoff::exec(VMFnArgs* args) {
642 int cutoff = args->arg(1)->asInt()->evalInt();
643 if (cutoff > VM_FILTER_PAR_MAX_VALUE) {
644 wrnMsg("change_cutoff(): argument 2 may not be larger than 1000000");
645 cutoff = VM_FILTER_PAR_MAX_VALUE;
646 } else if (cutoff < 0) {
647 wrnMsg("change_cutoff(): argument 2 may not be negative");
648 cutoff = 0;
649 }
650 const float fCutoff = float(cutoff) / float(VM_FILTER_PAR_MAX_VALUE);
651
652 AbstractEngineChannel* pEngineChannel =
653 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
654
655 if (args->arg(0)->exprType() == INT_EXPR) {
656 const ScriptID id = args->arg(0)->asInt()->evalInt();
657 if (!id) {
658 wrnMsg("change_cutoff(): note ID for argument 1 may not be zero");
659 return successResult();
660 }
661 if (!id.isNoteID()) {
662 wrnMsg("change_cutoff(): argument 1 is not a note ID");
663 return successResult();
664 }
665
666 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
667 if (!pNote) return successResult();
668
669 // if change_cutoff() was called immediately after note was triggered
670 // then immediately apply cutoff to Note object
671 if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
672 pNote->Override.Cutoff = fCutoff;
673 } else { // otherwise schedule cutoff change ...
674 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
675 e.Init(); // clear IDs
676 e.Type = Event::type_note_synth_param;
677 e.Param.NoteSynthParam.NoteID = id.noteID();
678 e.Param.NoteSynthParam.Type = Event::synth_param_cutoff;
679 e.Param.NoteSynthParam.Delta = fCutoff;
680 e.Param.NoteSynthParam.Relative = false;
681
682 pEngineChannel->ScheduleEventMicroSec(&e, 0);
683 }
684 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
685 VMIntArrayExpr* ids = args->arg(0)->asIntArray();
686 for (int i = 0; i < ids->arraySize(); ++i) {
687 const ScriptID id = ids->evalIntElement(i);
688 if (!id || !id.isNoteID()) continue;
689
690 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
691 if (!pNote) continue;
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->cause.SchedTime() == 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 }
709 }
710
711 return successResult();
712 }
713
714 // change_reso() function
715
716 InstrumentScriptVMFunction_change_reso::InstrumentScriptVMFunction_change_reso(InstrumentScriptVM* parent)
717 : m_vm(parent)
718 {
719 }
720
721 bool InstrumentScriptVMFunction_change_reso::acceptsArgType(int iArg, ExprType_t type) const {
722 if (iArg == 0)
723 return type == INT_EXPR || type == INT_ARR_EXPR;
724 else
725 return INT_EXPR;
726 }
727
728 VMFnResult* InstrumentScriptVMFunction_change_reso::exec(VMFnArgs* args) {
729 int resonance = args->arg(1)->asInt()->evalInt();
730 if (resonance > VM_FILTER_PAR_MAX_VALUE) {
731 wrnMsg("change_reso(): argument 2 may not be larger than 1000000");
732 resonance = VM_FILTER_PAR_MAX_VALUE;
733 } else if (resonance < 0) {
734 wrnMsg("change_reso(): argument 2 may not be negative");
735 resonance = 0;
736 }
737 const float fResonance = float(resonance) / float(VM_FILTER_PAR_MAX_VALUE);
738
739 AbstractEngineChannel* pEngineChannel =
740 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
741
742 if (args->arg(0)->exprType() == INT_EXPR) {
743 const ScriptID id = args->arg(0)->asInt()->evalInt();
744 if (!id) {
745 wrnMsg("change_reso(): note ID for argument 1 may not be zero");
746 return successResult();
747 }
748 if (!id.isNoteID()) {
749 wrnMsg("change_reso(): argument 1 is not a note ID");
750 return successResult();
751 }
752
753 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
754 if (!pNote) return successResult();
755
756 // if change_reso() was called immediately after note was triggered
757 // then immediately apply resonance to Note object
758 if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
759 pNote->Override.Resonance = fResonance;
760 } else { // otherwise schedule resonance change ...
761 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
762 e.Init(); // clear IDs
763 e.Type = Event::type_note_synth_param;
764 e.Param.NoteSynthParam.NoteID = id.noteID();
765 e.Param.NoteSynthParam.Type = Event::synth_param_resonance;
766 e.Param.NoteSynthParam.Delta = fResonance;
767 e.Param.NoteSynthParam.Relative = false;
768
769 pEngineChannel->ScheduleEventMicroSec(&e, 0);
770 }
771 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
772 VMIntArrayExpr* ids = args->arg(0)->asIntArray();
773 for (int i = 0; i < ids->arraySize(); ++i) {
774 const ScriptID id = ids->evalIntElement(i);
775 if (!id || !id.isNoteID()) continue;
776
777 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
778 if (!pNote) continue;
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->cause.SchedTime() == 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 }
796 }
797
798 return successResult();
799 }
800
801 // change_attack() function
802
803 InstrumentScriptVMFunction_change_attack::InstrumentScriptVMFunction_change_attack(InstrumentScriptVM* parent)
804 : m_vm(parent)
805 {
806 }
807
808 bool InstrumentScriptVMFunction_change_attack::acceptsArgType(int iArg, ExprType_t type) const {
809 if (iArg == 0)
810 return type == INT_EXPR || type == INT_ARR_EXPR;
811 else
812 return INT_EXPR;
813 }
814
815 VMFnResult* InstrumentScriptVMFunction_change_attack::exec(VMFnArgs* args) {
816 int attack = args->arg(1)->asInt()->evalInt();
817 if (attack > VM_EG_PAR_MAX_VALUE) {
818 wrnMsg("change_attack(): argument 2 may not be larger than 1000000");
819 attack = VM_EG_PAR_MAX_VALUE;
820 } else if (attack < 0) {
821 wrnMsg("change_attack(): argument 2 may not be negative");
822 attack = 0;
823 }
824 const float fAttack = float(attack) / float(VM_EG_PAR_MAX_VALUE);
825
826 AbstractEngineChannel* pEngineChannel =
827 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
828
829 if (args->arg(0)->exprType() == INT_EXPR) {
830 const ScriptID id = args->arg(0)->asInt()->evalInt();
831 if (!id) {
832 wrnMsg("change_attack(): note ID for argument 1 may not be zero");
833 return successResult();
834 }
835 if (!id.isNoteID()) {
836 wrnMsg("change_attack(): argument 1 is not a note ID");
837 return successResult();
838 }
839
840 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
841 if (!pNote) return successResult();
842
843 // if change_attack() was called immediately after note was triggered
844 // then immediately apply attack to Note object
845 if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
846 pNote->Override.Attack = fAttack;
847 } else { // otherwise schedule attack change ...
848 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
849 e.Init(); // clear IDs
850 e.Type = Event::type_note_synth_param;
851 e.Param.NoteSynthParam.NoteID = id.noteID();
852 e.Param.NoteSynthParam.Type = Event::synth_param_attack;
853 e.Param.NoteSynthParam.Delta = fAttack;
854 e.Param.NoteSynthParam.Relative = false;
855
856 pEngineChannel->ScheduleEventMicroSec(&e, 0);
857 }
858 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
859 VMIntArrayExpr* ids = args->arg(0)->asIntArray();
860 for (int i = 0; i < ids->arraySize(); ++i) {
861 const ScriptID id = ids->evalIntElement(i);
862 if (!id || !id.isNoteID()) continue;
863
864 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
865 if (!pNote) continue;
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->cause.SchedTime() == 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 }
883 }
884
885 return successResult();
886 }
887
888 // change_decay() function
889
890 InstrumentScriptVMFunction_change_decay::InstrumentScriptVMFunction_change_decay(InstrumentScriptVM* parent)
891 : m_vm(parent)
892 {
893 }
894
895 bool InstrumentScriptVMFunction_change_decay::acceptsArgType(int iArg, ExprType_t type) const {
896 if (iArg == 0)
897 return type == INT_EXPR || type == INT_ARR_EXPR;
898 else
899 return INT_EXPR;
900 }
901
902 VMFnResult* InstrumentScriptVMFunction_change_decay::exec(VMFnArgs* args) {
903 int decay = args->arg(1)->asInt()->evalInt();
904 if (decay > VM_EG_PAR_MAX_VALUE) {
905 wrnMsg("change_decay(): argument 2 may not be larger than 1000000");
906 decay = VM_EG_PAR_MAX_VALUE;
907 } else if (decay < 0) {
908 wrnMsg("change_decay(): argument 2 may not be negative");
909 decay = 0;
910 }
911 const float fDecay = float(decay) / float(VM_EG_PAR_MAX_VALUE);
912
913 AbstractEngineChannel* pEngineChannel =
914 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
915
916 if (args->arg(0)->exprType() == INT_EXPR) {
917 const ScriptID id = args->arg(0)->asInt()->evalInt();
918 if (!id) {
919 wrnMsg("change_decay(): note ID for argument 1 may not be zero");
920 return successResult();
921 }
922 if (!id.isNoteID()) {
923 wrnMsg("change_decay(): argument 1 is not a note ID");
924 return successResult();
925 }
926
927 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
928 if (!pNote) return successResult();
929
930 // if change_decay() was called immediately after note was triggered
931 // then immediately apply decay to Note object
932 if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
933 pNote->Override.Decay = fDecay;
934 } else { // otherwise schedule decay change ...
935 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
936 e.Init(); // clear IDs
937 e.Type = Event::type_note_synth_param;
938 e.Param.NoteSynthParam.NoteID = id.noteID();
939 e.Param.NoteSynthParam.Type = Event::synth_param_decay;
940 e.Param.NoteSynthParam.Delta = fDecay;
941 e.Param.NoteSynthParam.Relative = false;
942
943 pEngineChannel->ScheduleEventMicroSec(&e, 0);
944 }
945 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
946 VMIntArrayExpr* ids = args->arg(0)->asIntArray();
947 for (int i = 0; i < ids->arraySize(); ++i) {
948 const ScriptID id = ids->evalIntElement(i);
949 if (!id || !id.isNoteID()) continue;
950
951 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
952 if (!pNote) continue;
953
954 // if change_decay() was called immediately after note was triggered
955 // then immediately apply decay to Note object
956 if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
957 pNote->Override.Decay = fDecay;
958 } else { // otherwise schedule decay change ...
959 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
960 e.Init(); // clear IDs
961 e.Type = Event::type_note_synth_param;
962 e.Param.NoteSynthParam.NoteID = id.noteID();
963 e.Param.NoteSynthParam.Type = Event::synth_param_decay;
964 e.Param.NoteSynthParam.Delta = fDecay;
965 e.Param.NoteSynthParam.Relative = false;
966
967 pEngineChannel->ScheduleEventMicroSec(&e, 0);
968 }
969 }
970 }
971
972 return successResult();
973 }
974
975 // change_release() function
976
977 InstrumentScriptVMFunction_change_release::InstrumentScriptVMFunction_change_release(InstrumentScriptVM* parent)
978 : m_vm(parent)
979 {
980 }
981
982 bool InstrumentScriptVMFunction_change_release::acceptsArgType(int iArg, ExprType_t type) const {
983 if (iArg == 0)
984 return type == INT_EXPR || type == INT_ARR_EXPR;
985 else
986 return INT_EXPR;
987 }
988
989 VMFnResult* InstrumentScriptVMFunction_change_release::exec(VMFnArgs* args) {
990 int release = args->arg(1)->asInt()->evalInt();
991 if (release > VM_EG_PAR_MAX_VALUE) {
992 wrnMsg("change_release(): argument 2 may not be larger than 1000000");
993 release = VM_EG_PAR_MAX_VALUE;
994 } else if (release < 0) {
995 wrnMsg("change_release(): argument 2 may not be negative");
996 release = 0;
997 }
998 const float fRelease = float(release) / float(VM_EG_PAR_MAX_VALUE);
999
1000 AbstractEngineChannel* pEngineChannel =
1001 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1002
1003 if (args->arg(0)->exprType() == INT_EXPR) {
1004 const ScriptID id = args->arg(0)->asInt()->evalInt();
1005 if (!id) {
1006 wrnMsg("change_release(): note ID for argument 1 may not be zero");
1007 return successResult();
1008 }
1009 if (!id.isNoteID()) {
1010 wrnMsg("change_release(): argument 1 is not a note ID");
1011 return successResult();
1012 }
1013
1014 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1015 if (!pNote) return successResult();
1016
1017 // if change_release() was called immediately after note was triggered
1018 // then immediately apply relase to Note object
1019 if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
1020 pNote->Override.Release = fRelease;
1021 } else { // otherwise schedule release change ...
1022 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1023 e.Init(); // clear IDs
1024 e.Type = Event::type_note_synth_param;
1025 e.Param.NoteSynthParam.NoteID = id.noteID();
1026 e.Param.NoteSynthParam.Type = Event::synth_param_release;
1027 e.Param.NoteSynthParam.Delta = fRelease;
1028 e.Param.NoteSynthParam.Relative = false;
1029
1030 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1031 }
1032 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1033 VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1034 for (int i = 0; i < ids->arraySize(); ++i) {
1035 const ScriptID id = ids->evalIntElement(i);
1036 if (!id || !id.isNoteID()) continue;
1037
1038 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1039 if (!pNote) continue;
1040
1041 // if change_release() was called immediately after note was triggered
1042 // then immediately apply relase to Note object
1043 if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
1044 pNote->Override.Release = fRelease;
1045 } else { // otherwise schedule release change ...
1046 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1047 e.Init(); // clear IDs
1048 e.Type = Event::type_note_synth_param;
1049 e.Param.NoteSynthParam.NoteID = id.noteID();
1050 e.Param.NoteSynthParam.Type = Event::synth_param_release;
1051 e.Param.NoteSynthParam.Delta = fRelease;
1052 e.Param.NoteSynthParam.Relative = false;
1053
1054 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1055 }
1056 }
1057 }
1058
1059 return successResult();
1060 }
1061
1062 #define VM_GENERAL_CHANGE_SYNTH_PAR_MAX_VALUE 1000000
1063
1064 bool VMChangeSynthParamFunction::acceptsArgType(int iArg, ExprType_t type) const {
1065 if (iArg == 0)
1066 return type == INT_EXPR || type == INT_ARR_EXPR;
1067 else
1068 return INT_EXPR;
1069 }
1070
1071 template<float NoteBase::_Override::*T_noteParam, int T_synthParam>
1072 VMFnResult* VMChangeSynthParamFunction::execTemplate(VMFnArgs* args, const char* functionName) {
1073 int value = args->arg(1)->asInt()->evalInt();
1074 if (value > VM_GENERAL_CHANGE_SYNTH_PAR_MAX_VALUE) {
1075 wrnMsg(String(functionName) + "(): argument 2 may not be larger than 1000000");
1076 value = VM_GENERAL_CHANGE_SYNTH_PAR_MAX_VALUE;
1077 } else if (value < 0) {
1078 wrnMsg(String(functionName) + "(): argument 2 may not be negative");
1079 value = 0;
1080 }
1081 const float fValue = float(value) / float(VM_GENERAL_CHANGE_SYNTH_PAR_MAX_VALUE);
1082
1083 AbstractEngineChannel* pEngineChannel =
1084 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1085
1086 if (args->arg(0)->exprType() == INT_EXPR) {
1087 const ScriptID id = args->arg(0)->asInt()->evalInt();
1088 if (!id) {
1089 wrnMsg(String(functionName) + "(): note ID for argument 1 may not be zero");
1090 return successResult();
1091 }
1092 if (!id.isNoteID()) {
1093 wrnMsg(String(functionName) + "(): argument 1 is not a note ID");
1094 return successResult();
1095 }
1096
1097 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1098 if (!pNote) return successResult();
1099
1100 // if this change_*() script function was called immediately after
1101 // note was triggered then immediately apply the synth parameter
1102 // change to Note object
1103 if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
1104 pNote->Override.*T_noteParam = fValue;
1105 } else { // otherwise schedule this synth parameter change ...
1106 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1107 e.Init(); // clear IDs
1108 e.Type = Event::type_note_synth_param;
1109 e.Param.NoteSynthParam.NoteID = id.noteID();
1110 e.Param.NoteSynthParam.Type = (Event::synth_param_t) T_synthParam;
1111 e.Param.NoteSynthParam.Delta = fValue;
1112 e.Param.NoteSynthParam.Relative = false;
1113
1114 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1115 }
1116 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1117 VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1118 for (int i = 0; i < ids->arraySize(); ++i) {
1119 const ScriptID id = ids->evalIntElement(i);
1120 if (!id || !id.isNoteID()) continue;
1121
1122 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1123 if (!pNote) continue;
1124
1125 // if this change_*() script function was called immediately after
1126 // note was triggered then immediately apply the synth parameter
1127 // change to Note object
1128 if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
1129 pNote->Override.*T_noteParam = fValue;
1130 } else { // otherwise schedule this synth parameter change ...
1131 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1132 e.Init(); // clear IDs
1133 e.Type = Event::type_note_synth_param;
1134 e.Param.NoteSynthParam.NoteID = id.noteID();
1135 e.Param.NoteSynthParam.Type = (Event::synth_param_t) T_synthParam;
1136 e.Param.NoteSynthParam.Delta = fValue;
1137 e.Param.NoteSynthParam.Relative = false;
1138
1139 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1140 }
1141 }
1142 }
1143
1144 return successResult();
1145 }
1146
1147 // change_amp_lfo_depth() function
1148
1149 VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_depth::exec(VMFnArgs* args) {
1150 return VMChangeSynthParamFunction::execTemplate< &NoteBase::_Override::AmpLFODepth, Event::synth_param_amp_lfo_depth >(args, "change_amp_lfo_depth");
1151 }
1152
1153 // change_amp_lfo_freq() function
1154
1155 VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_freq::exec(VMFnArgs* args) {
1156 return VMChangeSynthParamFunction::execTemplate< &NoteBase::_Override::AmpLFOFreq, Event::synth_param_amp_lfo_freq >(args, "change_amp_lfo_freq");
1157 }
1158
1159 // change_pitch_lfo_depth() function
1160
1161 VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_depth::exec(VMFnArgs* args) {
1162 return VMChangeSynthParamFunction::execTemplate< &NoteBase::_Override::PitchLFODepth, Event::synth_param_pitch_lfo_depth >(args, "change_pitch_lfo_depth");
1163 }
1164
1165 // change_pitch_lfo_freq() function
1166
1167 VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_freq::exec(VMFnArgs* args) {
1168 return VMChangeSynthParamFunction::execTemplate< &NoteBase::_Override::PitchLFOFreq, Event::synth_param_pitch_lfo_freq >(args, "change_pitch_lfo_freq");
1169 }
1170
1171 // event_status() function
1172
1173 InstrumentScriptVMFunction_event_status::InstrumentScriptVMFunction_event_status(InstrumentScriptVM* parent)
1174 : m_vm(parent)
1175 {
1176 }
1177
1178 VMFnResult* InstrumentScriptVMFunction_event_status::exec(VMFnArgs* args) {
1179 AbstractEngineChannel* pEngineChannel =
1180 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1181
1182 const ScriptID id = args->arg(0)->asInt()->evalInt();
1183 if (!id) {
1184 wrnMsg("event_status(): note ID for argument 1 may not be zero");
1185 return successResult(EVENT_STATUS_INACTIVE);
1186 }
1187 if (!id.isNoteID()) {
1188 wrnMsg("event_status(): argument 1 is not a note ID");
1189 return successResult(EVENT_STATUS_INACTIVE);
1190 }
1191
1192 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1193 return successResult(pNote ? EVENT_STATUS_NOTE_QUEUE : EVENT_STATUS_INACTIVE);
1194 }
1195
1196 // wait() function (overrides core wait() implementation)
1197
1198 InstrumentScriptVMFunction_wait::InstrumentScriptVMFunction_wait(InstrumentScriptVM* parent)
1199 : CoreVMFunction_wait(parent)
1200 {
1201 }
1202
1203 VMFnResult* InstrumentScriptVMFunction_wait::exec(VMFnArgs* args) {
1204 InstrumentScriptVM* m_vm = (InstrumentScriptVM*) vm;
1205
1206 // this might be set by passing 1 with the 2nd argument of built-in stop_wait() function
1207 if (m_vm->m_event->ignoreAllWaitCalls) return successResult();
1208
1209 return CoreVMFunction_wait::exec(args);
1210 }
1211
1212 // stop_wait() function
1213
1214 InstrumentScriptVMFunction_stop_wait::InstrumentScriptVMFunction_stop_wait(InstrumentScriptVM* parent)
1215 : m_vm(parent)
1216 {
1217 }
1218
1219 VMFnResult* InstrumentScriptVMFunction_stop_wait::exec(VMFnArgs* args) {
1220 AbstractEngineChannel* pEngineChannel =
1221 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1222
1223 const script_callback_id_t id = args->arg(0)->asInt()->evalInt();
1224 if (!id) {
1225 wrnMsg("stop_wait(): callback ID for argument 1 may not be zero");
1226 return successResult();
1227 }
1228
1229 RTList<ScriptEvent>::Iterator itCallback = pEngineChannel->ScriptCallbackByID(id);
1230 if (!itCallback) return successResult(); // ignore if callback is i.e. not alive anymore
1231
1232 const bool disableWaitForever =
1233 (args->argsCount() >= 2) ? (args->arg(1)->asInt()->evalInt() == 1) : false;
1234
1235 pEngineChannel->ScheduleResumeOfScriptCallback(
1236 itCallback, m_vm->m_event->cause.SchedTime(), disableWaitForever
1237 );
1238
1239 return successResult();
1240 }
1241
1242 } // namespace LinuxSampler

  ViewVC Help
Powered by ViewVC