/[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 3188 - (show annotations) (download)
Fri May 19 14:23:12 2017 UTC (6 years, 11 months ago) by schoenebeck
File size: 67220 byte(s)
* NKSP: Implemented built-in script function "change_vol_time()".
* NKSP: Implemented built-in script function "change_tune_time()".
* NKSP: Implemented built-in script function "fade_in()".
* NKSP: Implemented built-in script function "fade_out()".
* NKSP: Fixed acceptance of wrong data type of parameters passed to
  built-in script functions "change_vol()", "change_tune()",
  "change_pan()", "change_cutoff()", "change_reso()",
  "change_attack()", "change_decay()", "change_release()",
  "change_amp_lfo_depth()", "change_amp_lfo_freq()",
  "change_pitch_lfo_depth()" and "change_pitch_lfo_freq()".
* Bumped version (2.0.0.svn45).

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 type == INT_EXPR;
363 }
364
365 VMFnResult* InstrumentScriptVMFunction_change_vol::exec(VMFnArgs* args) {
366 int volume = args->arg(1)->asInt()->evalInt(); // volume change in milli dB
367 bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;
368 const float fVolumeLin = RTMath::DecibelToLinRatio(float(volume) / 1000.f);
369
370 AbstractEngineChannel* pEngineChannel =
371 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
372
373 if (args->arg(0)->exprType() == INT_EXPR) {
374 const ScriptID id = args->arg(0)->asInt()->evalInt();
375 if (!id) {
376 wrnMsg("change_vol(): note ID for argument 1 may not be zero");
377 return successResult();
378 }
379 if (!id.isNoteID()) {
380 wrnMsg("change_vol(): argument 1 is not a note ID");
381 return successResult();
382 }
383
384 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
385 if (!pNote) return successResult();
386
387 // if change_vol() was called immediately after note was triggered
388 // then immediately apply the volume to note object
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 type == 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 type == 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 type == 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 type == 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 type == 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 type == 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 type == 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 bool VMChangeSynthParamFunction::acceptsArgType(int iArg, ExprType_t type) const {
1063 if (iArg == 0)
1064 return type == INT_EXPR || type == INT_ARR_EXPR;
1065 else
1066 return type == INT_EXPR;
1067 }
1068
1069 template<float NoteBase::_Override::*T_noteParam, int T_synthParam,
1070 bool T_isNormalizedParam, int T_maxValue, int T_minValue>
1071 VMFnResult* VMChangeSynthParamFunction::execTemplate(VMFnArgs* args, const char* functionName) {
1072 int value = args->arg(1)->asInt()->evalInt();
1073 if (value > T_maxValue) {
1074 wrnMsg(String(functionName) + "(): argument 2 may not be larger than " + ToString(T_maxValue));
1075 value = T_maxValue;
1076 } else if (value < T_minValue) {
1077 if (T_minValue == 0)
1078 wrnMsg(String(functionName) + "(): argument 2 may not be negative");
1079 else
1080 wrnMsg(String(functionName) + "(): argument 2 may not be smaller than " + ToString(T_minValue));
1081 value = T_minValue;
1082 }
1083 const float fValue = (T_isNormalizedParam) ?
1084 float(value) / float(T_maxValue) : // convert to 0.0 .. 1.0 value range
1085 float(value) / 1000000.f; // assuming microseconds here, convert to seconds
1086
1087 AbstractEngineChannel* pEngineChannel =
1088 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1089
1090 if (args->arg(0)->exprType() == INT_EXPR) {
1091 const ScriptID id = args->arg(0)->asInt()->evalInt();
1092 if (!id) {
1093 wrnMsg(String(functionName) + "(): note ID for argument 1 may not be zero");
1094 return successResult();
1095 }
1096 if (!id.isNoteID()) {
1097 wrnMsg(String(functionName) + "(): argument 1 is not a note ID");
1098 return successResult();
1099 }
1100
1101 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1102 if (!pNote) return successResult();
1103
1104 // if this change_*() script function was called immediately after
1105 // note was triggered then immediately apply the synth parameter
1106 // change to Note object
1107 if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
1108 pNote->Override.*T_noteParam = fValue;
1109 } else { // otherwise schedule this synth parameter change ...
1110 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1111 e.Init(); // clear IDs
1112 e.Type = Event::type_note_synth_param;
1113 e.Param.NoteSynthParam.NoteID = id.noteID();
1114 e.Param.NoteSynthParam.Type = (Event::synth_param_t) T_synthParam;
1115 e.Param.NoteSynthParam.Delta = fValue;
1116 e.Param.NoteSynthParam.Relative = false;
1117
1118 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1119 }
1120 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1121 VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1122 for (int i = 0; i < ids->arraySize(); ++i) {
1123 const ScriptID id = ids->evalIntElement(i);
1124 if (!id || !id.isNoteID()) continue;
1125
1126 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1127 if (!pNote) continue;
1128
1129 // if this change_*() script function was called immediately after
1130 // note was triggered then immediately apply the synth parameter
1131 // change to Note object
1132 if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
1133 pNote->Override.*T_noteParam = fValue;
1134 } else { // otherwise schedule this synth parameter change ...
1135 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1136 e.Init(); // clear IDs
1137 e.Type = Event::type_note_synth_param;
1138 e.Param.NoteSynthParam.NoteID = id.noteID();
1139 e.Param.NoteSynthParam.Type = (Event::synth_param_t) T_synthParam;
1140 e.Param.NoteSynthParam.Delta = fValue;
1141 e.Param.NoteSynthParam.Relative = false;
1142
1143 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1144 }
1145 }
1146 }
1147
1148 return successResult();
1149 }
1150
1151 // change_amp_lfo_depth() function
1152
1153 VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_depth::exec(VMFnArgs* args) {
1154 return VMChangeSynthParamFunction::execTemplate<
1155 &NoteBase::_Override::AmpLFODepth,
1156 Event::synth_param_amp_lfo_depth, true>( args, "change_amp_lfo_depth" );
1157 }
1158
1159 // change_amp_lfo_freq() function
1160
1161 VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_freq::exec(VMFnArgs* args) {
1162 return VMChangeSynthParamFunction::execTemplate<
1163 &NoteBase::_Override::AmpLFOFreq,
1164 Event::synth_param_amp_lfo_freq, true>( args, "change_amp_lfo_freq" );
1165 }
1166
1167 // change_pitch_lfo_depth() function
1168
1169 VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_depth::exec(VMFnArgs* args) {
1170 return VMChangeSynthParamFunction::execTemplate<
1171 &NoteBase::_Override::PitchLFODepth,
1172 Event::synth_param_pitch_lfo_depth, true>( args, "change_pitch_lfo_depth" );
1173 }
1174
1175 // change_pitch_lfo_freq() function
1176
1177 VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_freq::exec(VMFnArgs* args) {
1178 return VMChangeSynthParamFunction::execTemplate<
1179 &NoteBase::_Override::PitchLFOFreq,
1180 Event::synth_param_pitch_lfo_freq, true>( args, "change_pitch_lfo_freq" );
1181 }
1182
1183 // change_vol_time() function
1184
1185 VMFnResult* InstrumentScriptVMFunction_change_vol_time::exec(VMFnArgs* args) {
1186 return VMChangeSynthParamFunction::execTemplate<
1187 &NoteBase::_Override::VolumeTime,
1188 Event::synth_param_volume_time, false>( args, "change_vol_time" );
1189 }
1190
1191 // change_tune_time() function
1192
1193 VMFnResult* InstrumentScriptVMFunction_change_tune_time::exec(VMFnArgs* args) {
1194 return VMChangeSynthParamFunction::execTemplate<
1195 &NoteBase::_Override::PitchTime,
1196 Event::synth_param_pitch_time, false>( args, "change_tune_time" );
1197 }
1198
1199 // fade_in() function
1200
1201 InstrumentScriptVMFunction_fade_in::InstrumentScriptVMFunction_fade_in(InstrumentScriptVM* parent)
1202 : m_vm(parent)
1203 {
1204 }
1205
1206 bool InstrumentScriptVMFunction_fade_in::acceptsArgType(int iArg, ExprType_t type) const {
1207 if (iArg == 0)
1208 return type == INT_EXPR || type == INT_ARR_EXPR;
1209 else
1210 return type == INT_EXPR;
1211 }
1212
1213 VMFnResult* InstrumentScriptVMFunction_fade_in::exec(VMFnArgs* args) {
1214 int duration = args->arg(1)->asInt()->evalInt();
1215 if (duration < 0) {
1216 wrnMsg("fade_in(): argument 2 may not be negative");
1217 duration = 0;
1218 }
1219 const float fDuration = float(duration) / 1000000.f; // convert microseconds to seconds
1220
1221 AbstractEngineChannel* pEngineChannel =
1222 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1223
1224 if (args->arg(0)->exprType() == INT_EXPR) {
1225 const ScriptID id = args->arg(0)->asInt()->evalInt();
1226 if (!id) {
1227 wrnMsg("fade_in(): note ID for argument 1 may not be zero");
1228 return successResult();
1229 }
1230 if (!id.isNoteID()) {
1231 wrnMsg("fade_in(): argument 1 is not a note ID");
1232 return successResult();
1233 }
1234
1235 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1236 if (!pNote) return successResult();
1237
1238 // if fade_in() was called immediately after note was triggered
1239 // then immediately apply a start volume of zero to Note object,
1240 // as well as the fade in duration
1241 if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
1242 pNote->Override.Volume = 0.f;
1243 pNote->Override.VolumeTime = fDuration;
1244 } else { // otherwise schedule a "volume time" change with the requested fade in duration ...
1245 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1246 e.Init(); // clear IDs
1247 e.Type = Event::type_note_synth_param;
1248 e.Param.NoteSynthParam.NoteID = id.noteID();
1249 e.Param.NoteSynthParam.Type = Event::synth_param_volume_time;
1250 e.Param.NoteSynthParam.Delta = fDuration;
1251 e.Param.NoteSynthParam.Relative = false;
1252
1253 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1254 }
1255 // and finally schedule a "volume" change, simply one time slice
1256 // ahead, with the final fade in volume (1.0)
1257 {
1258 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1259 e.Init(); // clear IDs
1260 e.Type = Event::type_note_synth_param;
1261 e.Param.NoteSynthParam.NoteID = id.noteID();
1262 e.Param.NoteSynthParam.Type = Event::synth_param_volume;
1263 e.Param.NoteSynthParam.Delta = 1.f;
1264 e.Param.NoteSynthParam.Relative = false;
1265
1266 // scheduling with 0 delay would also work here, but +1 is more
1267 // safe regarding potential future implementation changes of the
1268 // scheduler (see API comments of RTAVLTree::insert())
1269 pEngineChannel->ScheduleEventMicroSec(&e, 1);
1270 }
1271 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1272 VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1273 for (int i = 0; i < ids->arraySize(); ++i) {
1274 const ScriptID id = ids->evalIntElement(i);
1275 if (!id || !id.isNoteID()) continue;
1276
1277 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1278 if (!pNote) continue;
1279
1280 // if fade_in() was called immediately after note was triggered
1281 // then immediately apply a start volume of zero to Note object,
1282 // as well as the fade in duration
1283 if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
1284 pNote->Override.Volume = 0.f;
1285 pNote->Override.VolumeTime = fDuration;
1286 } else { // otherwise schedule a "volume time" change with the requested fade in duration ...
1287 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1288 e.Init(); // clear IDs
1289 e.Type = Event::type_note_synth_param;
1290 e.Param.NoteSynthParam.NoteID = id.noteID();
1291 e.Param.NoteSynthParam.Type = Event::synth_param_volume_time;
1292 e.Param.NoteSynthParam.Delta = fDuration;
1293 e.Param.NoteSynthParam.Relative = false;
1294
1295 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1296 }
1297 // and finally schedule a "volume" change, simply one time slice
1298 // ahead, with the final fade in volume (1.0)
1299 {
1300 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1301 e.Init(); // clear IDs
1302 e.Type = Event::type_note_synth_param;
1303 e.Param.NoteSynthParam.NoteID = id.noteID();
1304 e.Param.NoteSynthParam.Type = Event::synth_param_volume;
1305 e.Param.NoteSynthParam.Delta = 1.f;
1306 e.Param.NoteSynthParam.Relative = false;
1307
1308 // scheduling with 0 delay would also work here, but +1 is more
1309 // safe regarding potential future implementation changes of the
1310 // scheduler (see API comments of RTAVLTree::insert())
1311 pEngineChannel->ScheduleEventMicroSec(&e, 1);
1312 }
1313 }
1314 }
1315
1316 return successResult();
1317 }
1318
1319 // fade_out() function
1320
1321 InstrumentScriptVMFunction_fade_out::InstrumentScriptVMFunction_fade_out(InstrumentScriptVM* parent)
1322 : m_vm(parent)
1323 {
1324 }
1325
1326 bool InstrumentScriptVMFunction_fade_out::acceptsArgType(int iArg, ExprType_t type) const {
1327 if (iArg == 0)
1328 return type == INT_EXPR || type == INT_ARR_EXPR;
1329 else
1330 return type == INT_EXPR;
1331 }
1332
1333 VMFnResult* InstrumentScriptVMFunction_fade_out::exec(VMFnArgs* args) {
1334 int duration = args->arg(1)->asInt()->evalInt();
1335 if (duration < 0) {
1336 wrnMsg("fade_out(): argument 2 may not be negative");
1337 duration = 0;
1338 }
1339 const float fDuration = float(duration) / 1000000.f; // convert microseconds to seconds
1340
1341 bool stop = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : true;
1342
1343 AbstractEngineChannel* pEngineChannel =
1344 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1345
1346 if (args->arg(0)->exprType() == INT_EXPR) {
1347 const ScriptID id = args->arg(0)->asInt()->evalInt();
1348 if (!id) {
1349 wrnMsg("fade_out(): note ID for argument 1 may not be zero");
1350 return successResult();
1351 }
1352 if (!id.isNoteID()) {
1353 wrnMsg("fade_out(): argument 1 is not a note ID");
1354 return successResult();
1355 }
1356
1357 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1358 if (!pNote) return successResult();
1359
1360 // if fade_out() was called immediately after note was triggered
1361 // then immediately apply fade out duration to Note object
1362 if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
1363 pNote->Override.VolumeTime = fDuration;
1364 } else { // otherwise schedule a "volume time" change with the requested fade out duration ...
1365 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1366 e.Init(); // clear IDs
1367 e.Type = Event::type_note_synth_param;
1368 e.Param.NoteSynthParam.NoteID = id.noteID();
1369 e.Param.NoteSynthParam.Type = Event::synth_param_volume_time;
1370 e.Param.NoteSynthParam.Delta = fDuration;
1371 e.Param.NoteSynthParam.Relative = false;
1372
1373 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1374 }
1375 // now schedule a "volume" change, simply one time slice ahead, with
1376 // the final fade out volume (0.0)
1377 {
1378 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1379 e.Init(); // clear IDs
1380 e.Type = Event::type_note_synth_param;
1381 e.Param.NoteSynthParam.NoteID = id.noteID();
1382 e.Param.NoteSynthParam.Type = Event::synth_param_volume;
1383 e.Param.NoteSynthParam.Delta = 0.f;
1384 e.Param.NoteSynthParam.Relative = false;
1385
1386 // scheduling with 0 delay would also work here, but +1 is more
1387 // safe regarding potential future implementation changes of the
1388 // scheduler (see API comments of RTAVLTree::insert())
1389 pEngineChannel->ScheduleEventMicroSec(&e, 1);
1390 }
1391 // and finally if stopping the note was requested after the fade out
1392 // completed, then schedule to kill the voice after the requested
1393 // duration
1394 if (stop) {
1395 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1396 e.Init(); // clear IDs
1397 e.Type = Event::type_kill_note;
1398 e.Param.Note.ID = id.noteID();
1399 e.Param.Note.Key = pNote->hostKey;
1400
1401 pEngineChannel->ScheduleEventMicroSec(&e, duration + 1);
1402 }
1403 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1404 VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1405 for (int i = 0; i < ids->arraySize(); ++i) {
1406 const ScriptID id = ids->evalIntElement(i);
1407 if (!id || !id.isNoteID()) continue;
1408
1409 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1410 if (!pNote) continue;
1411
1412 // if fade_out() was called immediately after note was triggered
1413 // then immediately apply fade out duration to Note object
1414 if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
1415 pNote->Override.VolumeTime = fDuration;
1416 } else { // otherwise schedule a "volume time" change with the requested fade out duration ...
1417 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1418 e.Init(); // clear IDs
1419 e.Type = Event::type_note_synth_param;
1420 e.Param.NoteSynthParam.NoteID = id.noteID();
1421 e.Param.NoteSynthParam.Type = Event::synth_param_volume_time;
1422 e.Param.NoteSynthParam.Delta = fDuration;
1423 e.Param.NoteSynthParam.Relative = false;
1424
1425 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1426 }
1427 // now schedule a "volume" change, simply one time slice ahead, with
1428 // the final fade out volume (0.0)
1429 {
1430 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1431 e.Init(); // clear IDs
1432 e.Type = Event::type_note_synth_param;
1433 e.Param.NoteSynthParam.NoteID = id.noteID();
1434 e.Param.NoteSynthParam.Type = Event::synth_param_volume;
1435 e.Param.NoteSynthParam.Delta = 0.f;
1436 e.Param.NoteSynthParam.Relative = false;
1437
1438 // scheduling with 0 delay would also work here, but +1 is more
1439 // safe regarding potential future implementation changes of the
1440 // scheduler (see API comments of RTAVLTree::insert())
1441 pEngineChannel->ScheduleEventMicroSec(&e, 1);
1442 }
1443 // and finally if stopping the note was requested after the fade out
1444 // completed, then schedule to kill the voice after the requested
1445 // duration
1446 if (stop) {
1447 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1448 e.Init(); // clear IDs
1449 e.Type = Event::type_kill_note;
1450 e.Param.Note.ID = id.noteID();
1451 e.Param.Note.Key = pNote->hostKey;
1452
1453 pEngineChannel->ScheduleEventMicroSec(&e, duration + 1);
1454 }
1455 }
1456 }
1457
1458 return successResult();
1459 }
1460
1461 // event_status() function
1462
1463 InstrumentScriptVMFunction_event_status::InstrumentScriptVMFunction_event_status(InstrumentScriptVM* parent)
1464 : m_vm(parent)
1465 {
1466 }
1467
1468 VMFnResult* InstrumentScriptVMFunction_event_status::exec(VMFnArgs* args) {
1469 AbstractEngineChannel* pEngineChannel =
1470 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1471
1472 const ScriptID id = args->arg(0)->asInt()->evalInt();
1473 if (!id) {
1474 wrnMsg("event_status(): note ID for argument 1 may not be zero");
1475 return successResult(EVENT_STATUS_INACTIVE);
1476 }
1477 if (!id.isNoteID()) {
1478 wrnMsg("event_status(): argument 1 is not a note ID");
1479 return successResult(EVENT_STATUS_INACTIVE);
1480 }
1481
1482 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1483 return successResult(pNote ? EVENT_STATUS_NOTE_QUEUE : EVENT_STATUS_INACTIVE);
1484 }
1485
1486 // wait() function (overrides core wait() implementation)
1487
1488 InstrumentScriptVMFunction_wait::InstrumentScriptVMFunction_wait(InstrumentScriptVM* parent)
1489 : CoreVMFunction_wait(parent)
1490 {
1491 }
1492
1493 VMFnResult* InstrumentScriptVMFunction_wait::exec(VMFnArgs* args) {
1494 InstrumentScriptVM* m_vm = (InstrumentScriptVM*) vm;
1495
1496 // this might be set by passing 1 with the 2nd argument of built-in stop_wait() function
1497 if (m_vm->m_event->ignoreAllWaitCalls) return successResult();
1498
1499 return CoreVMFunction_wait::exec(args);
1500 }
1501
1502 // stop_wait() function
1503
1504 InstrumentScriptVMFunction_stop_wait::InstrumentScriptVMFunction_stop_wait(InstrumentScriptVM* parent)
1505 : m_vm(parent)
1506 {
1507 }
1508
1509 VMFnResult* InstrumentScriptVMFunction_stop_wait::exec(VMFnArgs* args) {
1510 AbstractEngineChannel* pEngineChannel =
1511 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1512
1513 const script_callback_id_t id = args->arg(0)->asInt()->evalInt();
1514 if (!id) {
1515 wrnMsg("stop_wait(): callback ID for argument 1 may not be zero");
1516 return successResult();
1517 }
1518
1519 RTList<ScriptEvent>::Iterator itCallback = pEngineChannel->ScriptCallbackByID(id);
1520 if (!itCallback) return successResult(); // ignore if callback is i.e. not alive anymore
1521
1522 const bool disableWaitForever =
1523 (args->argsCount() >= 2) ? (args->arg(1)->asInt()->evalInt() == 1) : false;
1524
1525 pEngineChannel->ScheduleResumeOfScriptCallback(
1526 itCallback, m_vm->m_event->cause.SchedTime(), disableWaitForever
1527 );
1528
1529 return successResult();
1530 }
1531
1532 } // namespace LinuxSampler

  ViewVC Help
Powered by ViewVC