/[svn]/linuxsampler/trunk/src/engines/common/InstrumentScriptVMFunctions.cpp
ViewVC logotype

Annotation of /linuxsampler/trunk/src/engines/common/InstrumentScriptVMFunctions.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3743 - (hide annotations) (download)
Fri Feb 14 15:35:53 2020 UTC (4 years, 2 months ago) by schoenebeck
File size: 121967 byte(s)
- Just added some TODO comments.

- Minor white space cleanup.

1 schoenebeck 2596 /*
2 schoenebeck 3691 * Copyright (c) 2014-2020 Christian Schoenebeck
3 schoenebeck 2596 *
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 schoenebeck 3561 #include "../../common/global_private.h"
14 schoenebeck 2596
15     namespace LinuxSampler {
16 schoenebeck 3743
17 schoenebeck 2931 // play_note() function
18 schoenebeck 2596
19     InstrumentScriptVMFunction_play_note::InstrumentScriptVMFunction_play_note(InstrumentScriptVM* parent)
20     : m_vm(parent)
21     {
22     }
23    
24 schoenebeck 3587 bool InstrumentScriptVMFunction_play_note::acceptsArgType(vmint iArg, ExprType_t type) const {
25     if (iArg == 2 || iArg == 3)
26     return type == INT_EXPR || type == REAL_EXPR;
27     else
28     return type == INT_EXPR;
29     }
30    
31     bool InstrumentScriptVMFunction_play_note::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
32     if (iArg == 2 || iArg == 3)
33     return type == VM_NO_UNIT || type == VM_SECOND;
34     else
35     return type == VM_NO_UNIT;
36     }
37    
38     bool InstrumentScriptVMFunction_play_note::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
39     if (iArg == 2 || iArg == 3)
40     return type == VM_SECOND; // only allow metric prefix(es) if 'seconds' is used as unit type
41     else
42     return false;
43     }
44    
45 schoenebeck 3588 void InstrumentScriptVMFunction_play_note::checkArgs(VMFnArgs* args,
46     std::function<void(String)> err,
47     std::function<void(String)> wrn)
48     {
49     // super class checks
50     Super::checkArgs(args, err, wrn);
51    
52     // own checks ...
53     if (args->arg(0)->isConstExpr()) {
54     vmint note = args->arg(0)->asNumber()->evalCastInt();
55     if (note < 0 || note > 127) {
56     err("MIDI note number value for argument 1 must be between 0..127");
57     return;
58     }
59     }
60     if (args->argsCount() >= 2 && args->arg(1)->isConstExpr()) {
61     vmint velocity = args->arg(1)->asNumber()->evalCastInt();
62     if (velocity < 0 || velocity > 127) {
63     err("MIDI velocity value for argument 2 must be between 0..127");
64     return;
65     }
66     }
67     if (args->argsCount() >= 3 && args->arg(2)->isConstExpr()) {
68     VMNumberExpr* argSampleOffset = args->arg(2)->asNumber();
69     vmint sampleoffset =
70     (argSampleOffset->unitType()) ?
71     argSampleOffset->evalCastInt(VM_MICRO) :
72     argSampleOffset->evalCastInt();
73     if (sampleoffset < -1) {
74     err("Sample offset of argument 3 may not be less than -1");
75     return;
76     }
77     }
78     if (args->argsCount() >= 4 && args->arg(3)->isConstExpr()) {
79     VMNumberExpr* argDuration = args->arg(3)->asNumber();
80     vmint duration =
81     (argDuration->unitType()) ?
82     argDuration->evalCastInt(VM_MICRO) :
83     argDuration->evalCastInt();
84     if (duration < -2) {
85     err("Argument 4 must be a duration value of at least -2 or higher");
86     return;
87     }
88     }
89     }
90    
91 schoenebeck 2596 VMFnResult* InstrumentScriptVMFunction_play_note::exec(VMFnArgs* args) {
92 schoenebeck 3557 vmint note = args->arg(0)->asInt()->evalInt();
93     vmint velocity = (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : 127;
94 schoenebeck 3587 VMNumberExpr* argDuration = (args->argsCount() >= 4) ? args->arg(3)->asNumber() : NULL;
95     vmint duration =
96     (argDuration) ?
97     (argDuration->unitType()) ?
98     argDuration->evalCastInt(VM_MICRO) :
99     argDuration->evalCastInt() : 0; //TODO: -1 might be a better default value instead of 0
100 schoenebeck 2596
101     if (note < 0 || note > 127) {
102     errMsg("play_note(): argument 1 is an invalid note number");
103 schoenebeck 2879 return errorResult(0);
104 schoenebeck 2596 }
105    
106     if (velocity < 0 || velocity > 127) {
107     errMsg("play_note(): argument 2 is an invalid velocity value");
108 schoenebeck 2879 return errorResult(0);
109 schoenebeck 2596 }
110    
111 schoenebeck 3253 if (duration < -2) {
112     errMsg("play_note(): argument 4 must be a duration value of at least -2 or higher");
113 schoenebeck 2879 return errorResult(0);
114 schoenebeck 2596 }
115    
116     AbstractEngineChannel* pEngineChannel =
117     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
118    
119 schoenebeck 2879 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
120     e.Init(); // clear IDs
121 schoenebeck 2938 e.Type = Event::type_play_note;
122 schoenebeck 2596 e.Param.Note.Key = note;
123     e.Param.Note.Velocity = velocity;
124 schoenebeck 2879 // make this new note dependent to the life time of the original note
125     if (duration == -1) {
126     if (m_vm->currentVMEventHandler()->eventHandlerType() != VM_EVENT_HANDLER_NOTE) {
127     errMsg("play_note(): -1 for argument 4 may only be used for note event handlers");
128     return errorResult(0);
129     }
130     e.Param.Note.ParentNoteID = m_vm->m_event->cause.Param.Note.ID;
131 schoenebeck 3253 // check if that requested parent note is actually still alive
132     NoteBase* pParentNote =
133     pEngineChannel->pEngine->NoteByID( e.Param.Note.ParentNoteID );
134     // if parent note is already gone then this new note is not required anymore
135     if (!pParentNote)
136     return successResult(0);
137 schoenebeck 2879 }
138 schoenebeck 2596
139 schoenebeck 2879 const note_id_t id = pEngineChannel->ScheduleNoteMicroSec(&e, 0);
140 schoenebeck 2596
141 schoenebeck 3251 // if a sample offset is supplied, assign the offset as override
142     // to the previously created Note object
143     if (args->argsCount() >= 3) {
144 schoenebeck 3587 VMNumberExpr* argSampleOffset = args->arg(2)->asNumber();
145     vmint sampleoffset =
146     (argSampleOffset->unitType()) ?
147     argSampleOffset->evalCastInt(VM_MICRO) :
148     argSampleOffset->evalCastInt();
149 schoenebeck 3251 if (sampleoffset >= 0) {
150     NoteBase* pNote = pEngineChannel->pEngine->NoteByID(id);
151     if (pNote) {
152 schoenebeck 3557 pNote->Override.SampleOffset =
153     (decltype(pNote->Override.SampleOffset)) sampleoffset;
154 schoenebeck 3251 }
155     } else if (sampleoffset < -1) {
156     errMsg("play_note(): sample offset of argument 3 may not be less than -1");
157     }
158     }
159    
160 schoenebeck 2938 // if a duration is supplied (and play-note event was scheduled
161     // successfully above), then schedule a subsequent stop-note event
162 schoenebeck 2879 if (id && duration > 0) {
163 schoenebeck 2938 e.Type = Event::type_stop_note;
164     e.Param.Note.ID = id;
165 schoenebeck 2871 e.Param.Note.Velocity = 127;
166     pEngineChannel->ScheduleEventMicroSec(&e, duration);
167     }
168    
169 schoenebeck 2879 // even if id is null, don't return an errorResult() here, because that
170     // would abort the script, and under heavy load it may be considerable
171     // that ScheduleNoteMicroSec() fails above, so simply ignore that
172     return successResult( ScriptID::fromNoteID(id) );
173 schoenebeck 2598 }
174    
175 schoenebeck 2931 // set_controller() function
176    
177 schoenebeck 2600 InstrumentScriptVMFunction_set_controller::InstrumentScriptVMFunction_set_controller(InstrumentScriptVM* parent)
178     : m_vm(parent)
179     {
180     }
181    
182     VMFnResult* InstrumentScriptVMFunction_set_controller::exec(VMFnArgs* args) {
183 schoenebeck 3557 vmint controller = args->arg(0)->asInt()->evalInt();
184     vmint value = args->arg(1)->asInt()->evalInt();
185 schoenebeck 2600
186     AbstractEngineChannel* pEngineChannel =
187     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
188    
189 schoenebeck 2879 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
190     e.Init(); // clear IDs
191 schoenebeck 2600 if (controller == CTRL_TABLE_IDX_AFTERTOUCH) {
192     e.Type = Event::type_channel_pressure;
193     e.Param.ChannelPressure.Value = value & 127;
194     } else if (controller == CTRL_TABLE_IDX_PITCHBEND) {
195     e.Type = Event::type_pitchbend;
196     e.Param.Pitch.Pitch = value;
197     } else if (controller >= 0 && controller <= 127) {
198     e.Type = Event::type_control_change;
199     e.Param.CC.Controller = controller;
200     e.Param.CC.Value = value;
201     } else {
202     errMsg("set_controller(): argument 1 is an invalid controller");
203     return errorResult();
204     }
205    
206 schoenebeck 2879 const event_id_t id = pEngineChannel->ScheduleEventMicroSec(&e, 0);
207 schoenebeck 2600
208 schoenebeck 2879 // even if id is null, don't return an errorResult() here, because that
209     // would abort the script, and under heavy load it may be considerable
210     // that ScheduleEventMicroSec() fails above, so simply ignore that
211     return successResult( ScriptID::fromEventID(id) );
212 schoenebeck 2931 }
213 schoenebeck 2600
214 schoenebeck 3691 // set_rpn() function
215    
216     InstrumentScriptVMFunction_set_rpn::InstrumentScriptVMFunction_set_rpn(InstrumentScriptVM* parent)
217     : m_vm(parent)
218     {
219     }
220    
221     VMFnResult* InstrumentScriptVMFunction_set_rpn::exec(VMFnArgs* args) {
222     vmint parameter = args->arg(0)->asInt()->evalInt();
223     vmint value = args->arg(1)->asInt()->evalInt();
224    
225     if (parameter < 0 || parameter > 16383) {
226     errMsg("set_rpn(): argument 1 exceeds RPN parameter number range");
227     return errorResult();
228     }
229     if (value < 0 || value > 16383) {
230     errMsg("set_rpn(): argument 2 exceeds RPN value range");
231     return errorResult();
232     }
233    
234     AbstractEngineChannel* pEngineChannel =
235     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
236    
237     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
238     e.Init(); // clear IDs
239     e.Type = Event::type_rpn;
240     e.Param.RPN.Parameter = parameter;
241     e.Param.RPN.Value = value;
242    
243     const event_id_t id = pEngineChannel->ScheduleEventMicroSec(&e, 0);
244    
245     // even if id is null, don't return an errorResult() here, because that
246     // would abort the script, and under heavy load it may be considerable
247     // that ScheduleEventMicroSec() fails above, so simply ignore that
248     return successResult( ScriptID::fromEventID(id) );
249     }
250    
251     // set_nrpn() function
252    
253     InstrumentScriptVMFunction_set_nrpn::InstrumentScriptVMFunction_set_nrpn(InstrumentScriptVM* parent)
254     : m_vm(parent)
255     {
256     }
257    
258     VMFnResult* InstrumentScriptVMFunction_set_nrpn::exec(VMFnArgs* args) {
259     vmint parameter = args->arg(0)->asInt()->evalInt();
260     vmint value = args->arg(1)->asInt()->evalInt();
261    
262     if (parameter < 0 || parameter > 16383) {
263     errMsg("set_nrpn(): argument 1 exceeds NRPN parameter number range");
264     return errorResult();
265     }
266     if (value < 0 || value > 16383) {
267     errMsg("set_nrpn(): argument 2 exceeds NRPN value range");
268     return errorResult();
269     }
270    
271     AbstractEngineChannel* pEngineChannel =
272     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
273    
274     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
275     e.Init(); // clear IDs
276     e.Type = Event::type_nrpn;
277     e.Param.NRPN.Parameter = parameter;
278     e.Param.NRPN.Value = value;
279    
280     const event_id_t id = pEngineChannel->ScheduleEventMicroSec(&e, 0);
281    
282     // even if id is null, don't return an errorResult() here, because that
283     // would abort the script, and under heavy load it may be considerable
284     // that ScheduleEventMicroSec() fails above, so simply ignore that
285     return successResult( ScriptID::fromEventID(id) );
286     }
287    
288 schoenebeck 2931 // ignore_event() function
289    
290 schoenebeck 2598 InstrumentScriptVMFunction_ignore_event::InstrumentScriptVMFunction_ignore_event(InstrumentScriptVM* parent)
291     : m_vm(parent)
292     {
293     }
294    
295 schoenebeck 3557 bool InstrumentScriptVMFunction_ignore_event::acceptsArgType(vmint iArg, ExprType_t type) const {
296 schoenebeck 2630 return type == INT_EXPR || type == INT_ARR_EXPR;
297     }
298    
299 schoenebeck 2598 VMFnResult* InstrumentScriptVMFunction_ignore_event::exec(VMFnArgs* args) {
300     AbstractEngineChannel* pEngineChannel =
301 schoenebeck 2630 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
302 schoenebeck 2598
303 schoenebeck 3212 if (args->argsCount() == 0 || args->arg(0)->exprType() == INT_EXPR) {
304     const ScriptID id = (args->argsCount() >= 1) ? args->arg(0)->asInt()->evalInt() : m_vm->m_event->id;
305     if (!id && args->argsCount() >= 1) {
306 schoenebeck 2879 wrnMsg("ignore_event(): event ID argument may not be zero");
307     // not errorResult(), because that would abort the script, not intentional in this case
308 schoenebeck 2630 return successResult();
309     }
310 schoenebeck 2879 pEngineChannel->IgnoreEventByScriptID(id);
311 schoenebeck 2630 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
312     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
313     for (int i = 0; i < ids->arraySize(); ++i) {
314 schoenebeck 2879 const ScriptID id = ids->evalIntElement(i);
315     pEngineChannel->IgnoreEventByScriptID(id);
316 schoenebeck 2630 }
317     }
318 schoenebeck 2598
319 schoenebeck 2596 return successResult();
320     }
321    
322 schoenebeck 2931 // ignore_controller() function
323    
324 schoenebeck 2598 InstrumentScriptVMFunction_ignore_controller::InstrumentScriptVMFunction_ignore_controller(InstrumentScriptVM* parent)
325     : m_vm(parent)
326     {
327     }
328    
329     VMFnResult* InstrumentScriptVMFunction_ignore_controller::exec(VMFnArgs* args) {
330 schoenebeck 2879 const ScriptID id = (args->argsCount() >= 1) ? args->arg(0)->asInt()->evalInt() : m_vm->m_event->id;
331     if (!id && args->argsCount() >= 1) {
332     wrnMsg("ignore_controller(): event ID argument may not be zero");
333 schoenebeck 2598 return successResult();
334     }
335    
336     AbstractEngineChannel* pEngineChannel =
337     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
338    
339 schoenebeck 2879 pEngineChannel->IgnoreEventByScriptID(id);
340 schoenebeck 2598
341     return successResult();
342     }
343    
344 schoenebeck 2931 // note_off() function
345    
346 schoenebeck 2629 InstrumentScriptVMFunction_note_off::InstrumentScriptVMFunction_note_off(InstrumentScriptVM* parent)
347     : m_vm(parent)
348     {
349     }
350    
351 schoenebeck 3557 bool InstrumentScriptVMFunction_note_off::acceptsArgType(vmint iArg, ExprType_t type) const {
352 schoenebeck 2630 return type == INT_EXPR || type == INT_ARR_EXPR;
353     }
354    
355 schoenebeck 3588 void InstrumentScriptVMFunction_note_off::checkArgs(VMFnArgs* args,
356     std::function<void(String)> err,
357     std::function<void(String)> wrn)
358     {
359     // super class checks
360     Super::checkArgs(args, err, wrn);
361    
362     // own checks ...
363     if (args->argsCount() >= 2 && args->arg(1)->isConstExpr() && args->arg(1)->exprType() == INT_EXPR) {
364     vmint velocity = args->arg(1)->asInt()->evalInt();
365     if (velocity < 0 || velocity > 127) {
366     err("MIDI velocity value for argument 2 must be between 0..127");
367     return;
368     }
369     }
370     }
371    
372 schoenebeck 2629 VMFnResult* InstrumentScriptVMFunction_note_off::exec(VMFnArgs* args) {
373 schoenebeck 2630 AbstractEngineChannel* pEngineChannel =
374     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
375    
376 schoenebeck 3557 vmint velocity = (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : 127;
377 schoenebeck 2630 if (velocity < 0 || velocity > 127) {
378     errMsg("note_off(): argument 2 is an invalid velocity value");
379     return errorResult();
380     }
381 schoenebeck 2629
382 schoenebeck 2630 if (args->arg(0)->exprType() == INT_EXPR) {
383 schoenebeck 2879 const ScriptID id = args->arg(0)->asInt()->evalInt();
384     if (!id) {
385     wrnMsg("note_off(): note ID for argument 1 may not be zero");
386 schoenebeck 2630 return successResult();
387     }
388 schoenebeck 2879 if (!id.isNoteID()) {
389     wrnMsg("note_off(): argument 1 is not a note ID");
390     return successResult();
391     }
392 schoenebeck 2630
393 schoenebeck 2879 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
394     if (!pNote) return successResult();
395 schoenebeck 2630
396 schoenebeck 2879 Event e = pNote->cause;
397     e.Init(); // clear IDs
398     e.CopyTimeFrom(m_vm->m_event->cause); // set fragment time for "now"
399 schoenebeck 2938 e.Type = Event::type_stop_note;
400     e.Param.Note.ID = id.noteID();
401     e.Param.Note.Key = pNote->hostKey;
402 schoenebeck 2630 e.Param.Note.Velocity = velocity;
403    
404 schoenebeck 2879 pEngineChannel->ScheduleEventMicroSec(&e, 0);
405 schoenebeck 2630 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
406     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
407 schoenebeck 3557 for (vmint i = 0; i < ids->arraySize(); ++i) {
408 schoenebeck 2879 const ScriptID id = ids->evalIntElement(i);
409     if (!id || !id.isNoteID()) continue;
410 schoenebeck 2630
411 schoenebeck 2879 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
412     if (!pNote) continue;
413 schoenebeck 2630
414 schoenebeck 2879 Event e = pNote->cause;
415     e.Init(); // clear IDs
416     e.CopyTimeFrom(m_vm->m_event->cause); // set fragment time for "now"
417 schoenebeck 2938 e.Type = Event::type_stop_note;
418     e.Param.Note.ID = id.noteID();
419     e.Param.Note.Key = pNote->hostKey;
420 schoenebeck 2630 e.Param.Note.Velocity = velocity;
421    
422 schoenebeck 2879 pEngineChannel->ScheduleEventMicroSec(&e, 0);
423 schoenebeck 2630 }
424 schoenebeck 2629 }
425    
426 schoenebeck 2630 return successResult();
427     }
428    
429 schoenebeck 2931 // set_event_mark() function
430    
431 schoenebeck 2630 InstrumentScriptVMFunction_set_event_mark::InstrumentScriptVMFunction_set_event_mark(InstrumentScriptVM* parent)
432     : m_vm(parent)
433     {
434     }
435    
436 schoenebeck 3588 void InstrumentScriptVMFunction_set_event_mark::checkArgs(VMFnArgs* args,
437     std::function<void(String)> err,
438     std::function<void(String)> wrn)
439     {
440     // super class checks
441     Super::checkArgs(args, err, wrn);
442    
443     // own checks ...
444     if (args->argsCount() >= 2 && args->arg(1)->isConstExpr()) {
445     const vmint groupID = args->arg(1)->asInt()->evalInt();
446     if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) {
447     err("Argument 2 value is an invalid group id.");
448     return;
449     }
450     }
451     }
452    
453 schoenebeck 2630 VMFnResult* InstrumentScriptVMFunction_set_event_mark::exec(VMFnArgs* args) {
454 schoenebeck 2879 const ScriptID id = args->arg(0)->asInt()->evalInt();
455 schoenebeck 3557 const vmint groupID = args->arg(1)->asInt()->evalInt();
456 schoenebeck 2630
457     if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) {
458     errMsg("set_event_mark(): argument 2 is an invalid group id");
459 schoenebeck 2629 return errorResult();
460     }
461    
462     AbstractEngineChannel* pEngineChannel =
463     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
464    
465 schoenebeck 2879 // check if the event/note still exists
466     switch (id.type()) {
467     case ScriptID::EVENT: {
468     RTList<Event>::Iterator itEvent = pEngineChannel->pEngine->EventByID( id.eventID() );
469     if (!itEvent) return successResult();
470     break;
471     }
472     case ScriptID::NOTE: {
473     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
474     if (!pNote) return successResult();
475     break;
476     }
477     }
478 schoenebeck 2629
479 schoenebeck 2879 pEngineChannel->pScript->eventGroups[groupID].insert(id);
480 schoenebeck 2629
481 schoenebeck 2630 return successResult();
482     }
483 schoenebeck 2629
484 schoenebeck 2931 // delete_event_mark() function
485    
486 schoenebeck 2630 InstrumentScriptVMFunction_delete_event_mark::InstrumentScriptVMFunction_delete_event_mark(InstrumentScriptVM* parent)
487     : m_vm(parent)
488     {
489     }
490    
491 schoenebeck 3588 void InstrumentScriptVMFunction_delete_event_mark::checkArgs(VMFnArgs* args,
492     std::function<void(String)> err,
493     std::function<void(String)> wrn)
494     {
495     // super class checks
496     Super::checkArgs(args, err, wrn);
497    
498     // own checks ...
499     if (args->argsCount() >= 2 && args->arg(1)->isConstExpr()) {
500     const vmint groupID = args->arg(1)->asInt()->evalInt();
501     if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) {
502     err("Argument 2 value is an invalid group id.");
503     return;
504     }
505     }
506     }
507    
508 schoenebeck 2630 VMFnResult* InstrumentScriptVMFunction_delete_event_mark::exec(VMFnArgs* args) {
509 schoenebeck 2879 const ScriptID id = args->arg(0)->asInt()->evalInt();
510 schoenebeck 3557 const vmint groupID = args->arg(1)->asInt()->evalInt();
511 schoenebeck 2630
512     if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) {
513     errMsg("delete_event_mark(): argument 2 is an invalid group id");
514     return errorResult();
515     }
516    
517     AbstractEngineChannel* pEngineChannel =
518     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
519    
520 schoenebeck 2879 pEngineChannel->pScript->eventGroups[groupID].erase(id);
521 schoenebeck 2630
522 schoenebeck 2629 return successResult();
523     }
524    
525 schoenebeck 2931 // by_marks() function
526    
527 schoenebeck 2630 InstrumentScriptVMFunction_by_marks::InstrumentScriptVMFunction_by_marks(InstrumentScriptVM* parent)
528     : m_vm(parent)
529     {
530     }
531    
532 schoenebeck 3557 vmint InstrumentScriptVMFunction_by_marks::Result::arraySize() const {
533 schoenebeck 2630 return eventGroup->size();
534     }
535    
536 schoenebeck 3557 vmint InstrumentScriptVMFunction_by_marks::Result::evalIntElement(vmuint i) {
537 schoenebeck 2630 return (*eventGroup)[i];
538     }
539    
540     VMFnResult* InstrumentScriptVMFunction_by_marks::errorResult() {
541     m_result.eventGroup = NULL;
542     m_result.flags = StmtFlags_t(STMT_ABORT_SIGNALLED | STMT_ERROR_OCCURRED);
543     return &m_result;
544     }
545    
546     VMFnResult* InstrumentScriptVMFunction_by_marks::successResult(EventGroup* eventGroup) {
547     m_result.eventGroup = eventGroup;
548     m_result.flags = STMT_SUCCESS;
549     return &m_result;
550     }
551    
552 schoenebeck 3588 void InstrumentScriptVMFunction_by_marks::checkArgs(VMFnArgs* args,
553     std::function<void(String)> err,
554     std::function<void(String)> wrn)
555     {
556     // super class checks
557     Super::checkArgs(args, err, wrn);
558    
559     // own checks ...
560     if (args->arg(0)->isConstExpr()) {
561     const vmint groupID = args->arg(0)->asInt()->evalInt();
562     if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) {
563     err("Argument value is an invalid group id.");
564     return;
565     }
566     }
567     }
568    
569 schoenebeck 2630 VMFnResult* InstrumentScriptVMFunction_by_marks::exec(VMFnArgs* args) {
570 schoenebeck 3557 vmint groupID = args->arg(0)->asInt()->evalInt();
571 schoenebeck 2630
572     if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) {
573     errMsg("by_marks(): argument is an invalid group id");
574     return errorResult();
575     }
576    
577     AbstractEngineChannel* pEngineChannel =
578     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
579    
580     return successResult( &pEngineChannel->pScript->eventGroups[groupID] );
581     }
582    
583 schoenebeck 2931 // change_vol() function
584    
585     InstrumentScriptVMFunction_change_vol::InstrumentScriptVMFunction_change_vol(InstrumentScriptVM* parent)
586     : m_vm(parent)
587     {
588     }
589    
590 schoenebeck 3557 bool InstrumentScriptVMFunction_change_vol::acceptsArgType(vmint iArg, ExprType_t type) const {
591 schoenebeck 2931 if (iArg == 0)
592     return type == INT_EXPR || type == INT_ARR_EXPR;
593 schoenebeck 3587 else if (iArg == 1)
594     return type == INT_EXPR || type == REAL_EXPR;
595 schoenebeck 2931 else
596 schoenebeck 3188 return type == INT_EXPR;
597 schoenebeck 2931 }
598    
599 schoenebeck 3561 bool InstrumentScriptVMFunction_change_vol::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
600     if (iArg == 1)
601     return type == VM_NO_UNIT || type == VM_BEL;
602     else
603     return type == VM_NO_UNIT;
604     }
605    
606 schoenebeck 3564 bool InstrumentScriptVMFunction_change_vol::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
607 schoenebeck 3587 return iArg == 1 && type == VM_BEL; // only allow metric prefix(es) if 'Bel' is used as unit type
608 schoenebeck 3561 }
609    
610     bool InstrumentScriptVMFunction_change_vol::acceptsArgFinal(vmint iArg) const {
611     return iArg == 1;
612     }
613    
614 schoenebeck 2931 VMFnResult* InstrumentScriptVMFunction_change_vol::exec(VMFnArgs* args) {
615 schoenebeck 3587 StdUnit_t unit = args->arg(1)->asNumber()->unitType();
616     vmint volume =
617     (unit) ?
618     args->arg(1)->asNumber()->evalCastInt(VM_MILLI,VM_DECI) :
619     args->arg(1)->asNumber()->evalCastInt(); // volume change in milli dB
620     bool isFinal = args->arg(1)->asNumber()->isFinal();
621 schoenebeck 2931 bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;
622 schoenebeck 2962 const float fVolumeLin = RTMath::DecibelToLinRatio(float(volume) / 1000.f);
623 schoenebeck 2931
624     AbstractEngineChannel* pEngineChannel =
625     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
626    
627     if (args->arg(0)->exprType() == INT_EXPR) {
628     const ScriptID id = args->arg(0)->asInt()->evalInt();
629     if (!id) {
630     wrnMsg("change_vol(): note ID for argument 1 may not be zero");
631     return successResult();
632     }
633     if (!id.isNoteID()) {
634     wrnMsg("change_vol(): argument 1 is not a note ID");
635     return successResult();
636     }
637    
638     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
639     if (!pNote) return successResult();
640    
641 schoenebeck 2962 // if change_vol() was called immediately after note was triggered
642 schoenebeck 3218 // then immediately apply the volume to note object, but only if
643     // change_vol_time() has not been called before
644     if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime &&
645     pNote->Override.VolumeTime <= DEFAULT_NOTE_VOLUME_TIME_S)
646     {
647 schoenebeck 2962 if (relative)
648 schoenebeck 3561 pNote->Override.Volume.Value *= fVolumeLin;
649 schoenebeck 2931 else
650 schoenebeck 3561 pNote->Override.Volume.Value = fVolumeLin;
651     pNote->Override.Volume.Final = isFinal;
652 schoenebeck 2962 } else { // otherwise schedule the volume change ...
653 schoenebeck 2931 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
654     e.Init(); // clear IDs
655     e.Type = Event::type_note_synth_param;
656     e.Param.NoteSynthParam.NoteID = id.noteID();
657     e.Param.NoteSynthParam.Type = Event::synth_param_volume;
658     e.Param.NoteSynthParam.Delta = fVolumeLin;
659 schoenebeck 3561 e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
660     isFinal, relative, unit
661     );
662 schoenebeck 2931 pEngineChannel->ScheduleEventMicroSec(&e, 0);
663     }
664 schoenebeck 2962 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
665     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
666 schoenebeck 3557 for (vmint i = 0; i < ids->arraySize(); ++i) {
667 schoenebeck 2962 const ScriptID id = ids->evalIntElement(i);
668     if (!id || !id.isNoteID()) continue;
669    
670     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
671     if (!pNote) continue;
672    
673     // if change_vol() was called immediately after note was triggered
674 schoenebeck 3218 // then immediately apply the volume to Note object, but only if
675     // change_vol_time() has not been called before
676     if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime &&
677     pNote->Override.VolumeTime <= DEFAULT_NOTE_VOLUME_TIME_S)
678     {
679 schoenebeck 2962 if (relative)
680 schoenebeck 3561 pNote->Override.Volume.Value *= fVolumeLin;
681 schoenebeck 2962 else
682 schoenebeck 3561 pNote->Override.Volume.Value = fVolumeLin;
683     pNote->Override.Volume.Final = isFinal;
684 schoenebeck 2962 } else { // otherwise schedule the volume change ...
685     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
686     e.Init(); // clear IDs
687     e.Type = Event::type_note_synth_param;
688     e.Param.NoteSynthParam.NoteID = id.noteID();
689     e.Param.NoteSynthParam.Type = Event::synth_param_volume;
690     e.Param.NoteSynthParam.Delta = fVolumeLin;
691 schoenebeck 3561 e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
692     isFinal, relative, unit
693     );
694 schoenebeck 2962 pEngineChannel->ScheduleEventMicroSec(&e, 0);
695     }
696     }
697 schoenebeck 2931 }
698    
699     return successResult();
700     }
701    
702     // change_tune() function
703    
704     InstrumentScriptVMFunction_change_tune::InstrumentScriptVMFunction_change_tune(InstrumentScriptVM* parent)
705     : m_vm(parent)
706     {
707     }
708    
709 schoenebeck 3557 bool InstrumentScriptVMFunction_change_tune::acceptsArgType(vmint iArg, ExprType_t type) const {
710 schoenebeck 2931 if (iArg == 0)
711     return type == INT_EXPR || type == INT_ARR_EXPR;
712 schoenebeck 3587 else if (iArg == 1)
713     return type == INT_EXPR || type == REAL_EXPR;
714 schoenebeck 2931 else
715 schoenebeck 3188 return type == INT_EXPR;
716 schoenebeck 2931 }
717    
718 schoenebeck 3564 bool InstrumentScriptVMFunction_change_tune::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
719 schoenebeck 3561 return iArg == 1;
720     }
721    
722     bool InstrumentScriptVMFunction_change_tune::acceptsArgFinal(vmint iArg) const {
723     return iArg == 1;
724     }
725    
726 schoenebeck 2931 VMFnResult* InstrumentScriptVMFunction_change_tune::exec(VMFnArgs* args) {
727 schoenebeck 3587 vmint tune =
728     (args->arg(1)->asNumber()->hasUnitFactorNow())
729     ? args->arg(1)->asNumber()->evalCastInt(VM_MILLI,VM_CENTI)
730     : args->arg(1)->asNumber()->evalCastInt(); // tuning change in milli cents
731     bool isFinal = args->arg(1)->asNumber()->isFinal();
732     StdUnit_t unit = args->arg(1)->asNumber()->unitType();
733 schoenebeck 2931 bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;
734 schoenebeck 2962 const float fFreqRatio = RTMath::CentsToFreqRatioUnlimited(float(tune) / 1000.f);
735 schoenebeck 2931
736     AbstractEngineChannel* pEngineChannel =
737     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
738    
739     if (args->arg(0)->exprType() == INT_EXPR) {
740     const ScriptID id = args->arg(0)->asInt()->evalInt();
741     if (!id) {
742     wrnMsg("change_tune(): note ID for argument 1 may not be zero");
743     return successResult();
744     }
745     if (!id.isNoteID()) {
746     wrnMsg("change_tune(): argument 1 is not a note ID");
747     return successResult();
748     }
749    
750     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
751     if (!pNote) return successResult();
752    
753 schoenebeck 2962 // if change_tune() was called immediately after note was triggered
754 schoenebeck 3218 // then immediately apply the tuning to Note object, but only if
755     // change_tune_time() has not been called before
756     if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime &&
757     pNote->Override.PitchTime <= DEFAULT_NOTE_PITCH_TIME_S)
758     {
759 schoenebeck 2962 if (relative)
760 schoenebeck 3561 pNote->Override.Pitch.Value *= fFreqRatio;
761 schoenebeck 2931 else
762 schoenebeck 3561 pNote->Override.Pitch.Value = fFreqRatio;
763     pNote->Override.Pitch.Final = isFinal;
764 schoenebeck 2962 } else { // otherwise schedule tuning change ...
765 schoenebeck 2931 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
766     e.Init(); // clear IDs
767     e.Type = Event::type_note_synth_param;
768     e.Param.NoteSynthParam.NoteID = id.noteID();
769     e.Param.NoteSynthParam.Type = Event::synth_param_pitch;
770     e.Param.NoteSynthParam.Delta = fFreqRatio;
771 schoenebeck 3561 e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
772     isFinal, relative, unit
773     );
774 schoenebeck 2931 pEngineChannel->ScheduleEventMicroSec(&e, 0);
775     }
776 schoenebeck 2962 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
777     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
778 schoenebeck 3557 for (vmint i = 0; i < ids->arraySize(); ++i) {
779 schoenebeck 2962 const ScriptID id = ids->evalIntElement(i);
780     if (!id || !id.isNoteID()) continue;
781    
782     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
783     if (!pNote) continue;
784    
785     // if change_tune() was called immediately after note was triggered
786 schoenebeck 3218 // then immediately apply the tuning to Note object, but only if
787     // change_tune_time() has not been called before
788     if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime &&
789     pNote->Override.PitchTime <= DEFAULT_NOTE_PITCH_TIME_S)
790     {
791 schoenebeck 2962 if (relative)
792 schoenebeck 3561 pNote->Override.Pitch.Value *= fFreqRatio;
793 schoenebeck 2962 else
794 schoenebeck 3561 pNote->Override.Pitch.Value = fFreqRatio;
795     pNote->Override.Pitch.Final = isFinal;
796 schoenebeck 2962 } else { // otherwise schedule tuning change ...
797     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
798     e.Init(); // clear IDs
799     e.Type = Event::type_note_synth_param;
800     e.Param.NoteSynthParam.NoteID = id.noteID();
801     e.Param.NoteSynthParam.Type = Event::synth_param_pitch;
802     e.Param.NoteSynthParam.Delta = fFreqRatio;
803 schoenebeck 3561 e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
804     isFinal, relative, unit
805     );
806 schoenebeck 2962 pEngineChannel->ScheduleEventMicroSec(&e, 0);
807     }
808     }
809 schoenebeck 2931 }
810    
811     return successResult();
812     }
813    
814     // change_pan() function
815    
816     InstrumentScriptVMFunction_change_pan::InstrumentScriptVMFunction_change_pan(InstrumentScriptVM* parent)
817     : m_vm(parent)
818     {
819     }
820    
821 schoenebeck 3557 bool InstrumentScriptVMFunction_change_pan::acceptsArgType(vmint iArg, ExprType_t type) const {
822 schoenebeck 2931 if (iArg == 0)
823     return type == INT_EXPR || type == INT_ARR_EXPR;
824     else
825 schoenebeck 3188 return type == INT_EXPR;
826 schoenebeck 2931 }
827    
828 schoenebeck 3561 bool InstrumentScriptVMFunction_change_pan::acceptsArgFinal(vmint iArg) const {
829     return iArg == 1;
830     }
831    
832 schoenebeck 2931 VMFnResult* InstrumentScriptVMFunction_change_pan::exec(VMFnArgs* args) {
833 schoenebeck 3561 vmint pan = args->arg(1)->asInt()->evalInt();
834     bool isFinal = args->arg(1)->asInt()->isFinal();
835 schoenebeck 2931 bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;
836    
837     if (pan > 1000) {
838     wrnMsg("change_pan(): argument 2 may not be larger than 1000");
839     pan = 1000;
840     } else if (pan < -1000) {
841     wrnMsg("change_pan(): argument 2 may not be smaller than -1000");
842     pan = -1000;
843     }
844 schoenebeck 2962 const float fPan = float(pan) / 1000.f;
845 schoenebeck 2931
846     AbstractEngineChannel* pEngineChannel =
847     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
848    
849     if (args->arg(0)->exprType() == INT_EXPR) {
850     const ScriptID id = args->arg(0)->asInt()->evalInt();
851     if (!id) {
852     wrnMsg("change_pan(): note ID for argument 1 may not be zero");
853     return successResult();
854     }
855     if (!id.isNoteID()) {
856     wrnMsg("change_pan(): argument 1 is not a note ID");
857     return successResult();
858     }
859    
860     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
861     if (!pNote) return successResult();
862    
863 schoenebeck 2962 // if change_pan() was called immediately after note was triggered
864     // then immediately apply the panning to Note object
865 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
866 schoenebeck 2962 if (relative) {
867 schoenebeck 3561 pNote->Override.Pan.Value = RTMath::RelativeSummedAvg(pNote->Override.Pan.Value, fPan, ++pNote->Override.Pan.Sources);
868 schoenebeck 2931 } else {
869 schoenebeck 3561 pNote->Override.Pan.Value = fPan;
870     pNote->Override.Pan.Sources = 1; // only relevant on subsequent change_pan() calls on same note with 'relative' being set
871 schoenebeck 2962 }
872 schoenebeck 3561 pNote->Override.Pan.Final = isFinal;
873 schoenebeck 2962 } else { // otherwise schedule panning change ...
874 schoenebeck 2931 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
875     e.Init(); // clear IDs
876     e.Type = Event::type_note_synth_param;
877     e.Param.NoteSynthParam.NoteID = id.noteID();
878     e.Param.NoteSynthParam.Type = Event::synth_param_pan;
879     e.Param.NoteSynthParam.Delta = fPan;
880 schoenebeck 3561 e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
881     isFinal, relative, false
882     );
883 schoenebeck 2931 pEngineChannel->ScheduleEventMicroSec(&e, 0);
884     }
885 schoenebeck 2962 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
886     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
887 schoenebeck 3557 for (vmint i = 0; i < ids->arraySize(); ++i) {
888 schoenebeck 2962 const ScriptID id = ids->evalIntElement(i);
889     if (!id || !id.isNoteID()) continue;
890    
891     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
892     if (!pNote) continue;
893    
894     // if change_pan() was called immediately after note was triggered
895     // then immediately apply the panning to Note object
896 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
897 schoenebeck 2962 if (relative) {
898 schoenebeck 3561 pNote->Override.Pan.Value = RTMath::RelativeSummedAvg(pNote->Override.Pan.Value, fPan, ++pNote->Override.Pan.Sources);
899 schoenebeck 2962 } else {
900 schoenebeck 3561 pNote->Override.Pan.Value = fPan;
901     pNote->Override.Pan.Sources = 1; // only relevant on subsequent change_pan() calls on same note with 'relative' being set
902 schoenebeck 2962 }
903 schoenebeck 3561 pNote->Override.Pan.Final = isFinal;
904 schoenebeck 2962 } else { // otherwise schedule panning change ...
905     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
906     e.Init(); // clear IDs
907     e.Type = Event::type_note_synth_param;
908     e.Param.NoteSynthParam.NoteID = id.noteID();
909     e.Param.NoteSynthParam.Type = Event::synth_param_pan;
910     e.Param.NoteSynthParam.Delta = fPan;
911 schoenebeck 3561 e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
912     isFinal, relative, false
913     );
914 schoenebeck 2962 pEngineChannel->ScheduleEventMicroSec(&e, 0);
915     }
916     }
917 schoenebeck 2931 }
918    
919     return successResult();
920     }
921    
922 schoenebeck 2935 #define VM_FILTER_PAR_MAX_VALUE 1000000
923 schoenebeck 3561 #define VM_FILTER_PAR_MAX_HZ 30000
924 schoenebeck 2953 #define VM_EG_PAR_MAX_VALUE 1000000
925 schoenebeck 2935
926     // change_cutoff() function
927    
928     InstrumentScriptVMFunction_change_cutoff::InstrumentScriptVMFunction_change_cutoff(InstrumentScriptVM* parent)
929     : m_vm(parent)
930     {
931     }
932    
933 schoenebeck 3557 bool InstrumentScriptVMFunction_change_cutoff::acceptsArgType(vmint iArg, ExprType_t type) const {
934 schoenebeck 2935 if (iArg == 0)
935     return type == INT_EXPR || type == INT_ARR_EXPR;
936 schoenebeck 3587 else if (iArg == 1)
937     return type == INT_EXPR || type == REAL_EXPR;
938 schoenebeck 2935 else
939 schoenebeck 3188 return type == INT_EXPR;
940 schoenebeck 2935 }
941    
942 schoenebeck 3561 bool InstrumentScriptVMFunction_change_cutoff::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
943     if (iArg == 1)
944     return type == VM_NO_UNIT || type == VM_HERTZ;
945     else
946     return type == VM_NO_UNIT;
947     }
948    
949 schoenebeck 3564 bool InstrumentScriptVMFunction_change_cutoff::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
950 schoenebeck 3587 return iArg == 1 && type == VM_HERTZ; // only allow metric prefix(es) if 'Hz' is used as unit type
951 schoenebeck 3561 }
952    
953     bool InstrumentScriptVMFunction_change_cutoff::acceptsArgFinal(vmint iArg) const {
954     return iArg == 1;
955     }
956    
957 schoenebeck 3588 void InstrumentScriptVMFunction_change_cutoff::checkArgs(VMFnArgs* args,
958     std::function<void(String)> err,
959     std::function<void(String)> wrn)
960     {
961     // super class checks
962     Super::checkArgs(args, err, wrn);
963    
964     // own checks ...
965     if (args->argsCount() >= 2) {
966     VMNumberExpr* argCutoff = args->arg(1)->asNumber();
967     if (argCutoff->unitType() && !argCutoff->isFinal()) {
968     wrn("Argument 2 implies 'final' value when using Hz as unit for cutoff frequency.");
969     }
970     }
971     }
972    
973 schoenebeck 2935 VMFnResult* InstrumentScriptVMFunction_change_cutoff::exec(VMFnArgs* args) {
974 schoenebeck 3588 const StdUnit_t unit = args->arg(1)->asNumber()->unitType();
975 schoenebeck 3587 vmint cutoff =
976     (unit) ?
977     args->arg(1)->asNumber()->evalCastInt(VM_NO_PREFIX) :
978     args->arg(1)->asNumber()->evalCastInt();
979 schoenebeck 3588 const bool isFinal =
980     (unit) ?
981     true : // imply 'final' value if unit type is used
982     args->arg(1)->asNumber()->isFinal();
983 schoenebeck 3715 // note: intentionally not checking against a max. value here if no unit!
984     // (to allow i.e. passing 2000000 for doubling cutoff frequency)
985     if (unit && cutoff > VM_FILTER_PAR_MAX_HZ) {
986 schoenebeck 3561 wrnMsg("change_cutoff(): argument 2 may not be larger than " strfy(VM_FILTER_PAR_MAX_HZ) " Hz");
987     cutoff = VM_FILTER_PAR_MAX_HZ;
988 schoenebeck 2935 } else if (cutoff < 0) {
989     wrnMsg("change_cutoff(): argument 2 may not be negative");
990     cutoff = 0;
991     }
992 schoenebeck 3561 const float fCutoff =
993     (unit) ? cutoff : float(cutoff) / float(VM_FILTER_PAR_MAX_VALUE);
994 schoenebeck 2935
995     AbstractEngineChannel* pEngineChannel =
996     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
997    
998     if (args->arg(0)->exprType() == INT_EXPR) {
999     const ScriptID id = args->arg(0)->asInt()->evalInt();
1000     if (!id) {
1001     wrnMsg("change_cutoff(): note ID for argument 1 may not be zero");
1002     return successResult();
1003     }
1004     if (!id.isNoteID()) {
1005     wrnMsg("change_cutoff(): argument 1 is not a note ID");
1006     return successResult();
1007     }
1008    
1009     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1010     if (!pNote) return successResult();
1011    
1012 schoenebeck 2962 // if change_cutoff() was called immediately after note was triggered
1013     // then immediately apply cutoff to Note object
1014 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1015 schoenebeck 3561 pNote->Override.Cutoff.Value = fCutoff;
1016     pNote->Override.Cutoff.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit);
1017 schoenebeck 2962 } else { // otherwise schedule cutoff change ...
1018     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1019     e.Init(); // clear IDs
1020     e.Type = Event::type_note_synth_param;
1021     e.Param.NoteSynthParam.NoteID = id.noteID();
1022     e.Param.NoteSynthParam.Type = Event::synth_param_cutoff;
1023     e.Param.NoteSynthParam.Delta = fCutoff;
1024 schoenebeck 3561 e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1025     isFinal, false, unit
1026     );
1027 schoenebeck 2962 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1028     }
1029 schoenebeck 2935 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1030     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1031 schoenebeck 3557 for (vmint i = 0; i < ids->arraySize(); ++i) {
1032 schoenebeck 2935 const ScriptID id = ids->evalIntElement(i);
1033     if (!id || !id.isNoteID()) continue;
1034    
1035     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1036     if (!pNote) continue;
1037    
1038 schoenebeck 2962 // if change_cutoff() was called immediately after note was triggered
1039     // then immediately apply cutoff to Note object
1040 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1041 schoenebeck 3561 pNote->Override.Cutoff.Value = fCutoff;
1042     pNote->Override.Cutoff.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit);
1043 schoenebeck 2962 } else { // otherwise schedule cutoff change ...
1044     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1045     e.Init(); // clear IDs
1046     e.Type = Event::type_note_synth_param;
1047     e.Param.NoteSynthParam.NoteID = id.noteID();
1048     e.Param.NoteSynthParam.Type = Event::synth_param_cutoff;
1049     e.Param.NoteSynthParam.Delta = fCutoff;
1050 schoenebeck 3561 e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1051     isFinal, false, unit
1052     );
1053 schoenebeck 2962 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1054     }
1055 schoenebeck 2935 }
1056     }
1057    
1058     return successResult();
1059     }
1060    
1061     // change_reso() function
1062 schoenebeck 3743
1063 schoenebeck 2935 InstrumentScriptVMFunction_change_reso::InstrumentScriptVMFunction_change_reso(InstrumentScriptVM* parent)
1064     : m_vm(parent)
1065     {
1066     }
1067    
1068 schoenebeck 3557 bool InstrumentScriptVMFunction_change_reso::acceptsArgType(vmint iArg, ExprType_t type) const {
1069 schoenebeck 2935 if (iArg == 0)
1070     return type == INT_EXPR || type == INT_ARR_EXPR;
1071     else
1072 schoenebeck 3188 return type == INT_EXPR;
1073 schoenebeck 2935 }
1074    
1075 schoenebeck 3561 bool InstrumentScriptVMFunction_change_reso::acceptsArgFinal(vmint iArg) const {
1076     return iArg == 1;
1077     }
1078    
1079 schoenebeck 2935 VMFnResult* InstrumentScriptVMFunction_change_reso::exec(VMFnArgs* args) {
1080 schoenebeck 3557 vmint resonance = args->arg(1)->asInt()->evalInt();
1081 schoenebeck 3561 bool isFinal = args->arg(1)->asInt()->isFinal();
1082 schoenebeck 3715 // note: intentionally not checking against a max. value here!
1083     // (to allow i.e. passing 2000000 for doubling the resonance)
1084     if (resonance < 0) {
1085 schoenebeck 2935 wrnMsg("change_reso(): argument 2 may not be negative");
1086     resonance = 0;
1087     }
1088 schoenebeck 2962 const float fResonance = float(resonance) / float(VM_FILTER_PAR_MAX_VALUE);
1089 schoenebeck 2935
1090     AbstractEngineChannel* pEngineChannel =
1091     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1092    
1093     if (args->arg(0)->exprType() == INT_EXPR) {
1094     const ScriptID id = args->arg(0)->asInt()->evalInt();
1095     if (!id) {
1096     wrnMsg("change_reso(): note ID for argument 1 may not be zero");
1097     return successResult();
1098     }
1099     if (!id.isNoteID()) {
1100     wrnMsg("change_reso(): argument 1 is not a note ID");
1101     return successResult();
1102     }
1103    
1104     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1105     if (!pNote) return successResult();
1106    
1107 schoenebeck 2962 // if change_reso() was called immediately after note was triggered
1108     // then immediately apply resonance to Note object
1109 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1110 schoenebeck 3561 pNote->Override.Resonance.Value = fResonance;
1111     pNote->Override.Resonance.Final = isFinal;
1112 schoenebeck 2962 } else { // otherwise schedule resonance change ...
1113     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1114     e.Init(); // clear IDs
1115     e.Type = Event::type_note_synth_param;
1116     e.Param.NoteSynthParam.NoteID = id.noteID();
1117     e.Param.NoteSynthParam.Type = Event::synth_param_resonance;
1118     e.Param.NoteSynthParam.Delta = fResonance;
1119 schoenebeck 3561 e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1120     isFinal, false, false
1121     );
1122 schoenebeck 2962 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1123     }
1124 schoenebeck 2935 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1125     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1126 schoenebeck 3557 for (vmint i = 0; i < ids->arraySize(); ++i) {
1127 schoenebeck 2935 const ScriptID id = ids->evalIntElement(i);
1128     if (!id || !id.isNoteID()) continue;
1129    
1130     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1131     if (!pNote) continue;
1132    
1133 schoenebeck 2962 // if change_reso() was called immediately after note was triggered
1134     // then immediately apply resonance to Note object
1135 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1136 schoenebeck 3561 pNote->Override.Resonance.Value = fResonance;
1137     pNote->Override.Resonance.Final = isFinal;
1138 schoenebeck 2962 } else { // otherwise schedule resonance change ...
1139     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1140     e.Init(); // clear IDs
1141     e.Type = Event::type_note_synth_param;
1142     e.Param.NoteSynthParam.NoteID = id.noteID();
1143     e.Param.NoteSynthParam.Type = Event::synth_param_resonance;
1144     e.Param.NoteSynthParam.Delta = fResonance;
1145 schoenebeck 3561 e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1146     isFinal, false, false
1147     );
1148 schoenebeck 2962 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1149     }
1150 schoenebeck 2935 }
1151     }
1152    
1153     return successResult();
1154     }
1155 schoenebeck 3743
1156 schoenebeck 2953 // change_attack() function
1157 schoenebeck 3743 //
1158     //TODO: Derive from generalized, shared template class
1159     // VMChangeSynthParamFunction instead (like e.g. change_cutoff_attack()
1160     // implementation below already does) to ease maintenance.
1161 schoenebeck 2935
1162 schoenebeck 2953 InstrumentScriptVMFunction_change_attack::InstrumentScriptVMFunction_change_attack(InstrumentScriptVM* parent)
1163     : m_vm(parent)
1164     {
1165     }
1166    
1167 schoenebeck 3557 bool InstrumentScriptVMFunction_change_attack::acceptsArgType(vmint iArg, ExprType_t type) const {
1168 schoenebeck 2953 if (iArg == 0)
1169     return type == INT_EXPR || type == INT_ARR_EXPR;
1170     else
1171 schoenebeck 3587 return type == INT_EXPR || type == REAL_EXPR;
1172 schoenebeck 2953 }
1173    
1174 schoenebeck 3561 bool InstrumentScriptVMFunction_change_attack::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
1175     if (iArg == 1)
1176     return type == VM_NO_UNIT || type == VM_SECOND;
1177     else
1178     return type == VM_NO_UNIT;
1179     }
1180    
1181 schoenebeck 3564 bool InstrumentScriptVMFunction_change_attack::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
1182 schoenebeck 3587 return iArg == 1 && type == VM_SECOND; // only allow metric prefix(es) if 'seconds' is used as unit type
1183 schoenebeck 3561 }
1184    
1185     bool InstrumentScriptVMFunction_change_attack::acceptsArgFinal(vmint iArg) const {
1186     return iArg == 1;
1187     }
1188    
1189 schoenebeck 3588 void InstrumentScriptVMFunction_change_attack::checkArgs(VMFnArgs* args,
1190     std::function<void(String)> err,
1191     std::function<void(String)> wrn)
1192     {
1193     // super class checks
1194     Super::checkArgs(args, err, wrn);
1195    
1196     // own checks ...
1197     if (args->argsCount() >= 2) {
1198     VMNumberExpr* argTime = args->arg(1)->asNumber();
1199     if (argTime->unitType() && !argTime->isFinal()) {
1200     wrn("Argument 2 implies 'final' value when using seconds as unit for attack time.");
1201     }
1202     }
1203     }
1204    
1205 schoenebeck 2953 VMFnResult* InstrumentScriptVMFunction_change_attack::exec(VMFnArgs* args) {
1206 schoenebeck 3588 const StdUnit_t unit = args->arg(1)->asNumber()->unitType();
1207 schoenebeck 3587 vmint attack =
1208     (unit) ?
1209     args->arg(1)->asNumber()->evalCastInt(VM_MICRO) :
1210     args->arg(1)->asNumber()->evalCastInt();
1211 schoenebeck 3588 const bool isFinal =
1212     (unit) ?
1213     true : // imply 'final' value if unit type is used
1214     args->arg(1)->asNumber()->isFinal();
1215 schoenebeck 3303 // note: intentionally not checking against a max. value here!
1216     // (to allow i.e. passing 2000000 for doubling the attack time)
1217     if (attack < 0) {
1218 schoenebeck 2953 wrnMsg("change_attack(): argument 2 may not be negative");
1219     attack = 0;
1220     }
1221 schoenebeck 3588 const float fAttack =
1222 schoenebeck 3742 (unit) ?
1223     float(attack) / 1000000.f /* us -> s */ :
1224     float(attack) / float(VM_EG_PAR_MAX_VALUE);
1225 schoenebeck 2953
1226     AbstractEngineChannel* pEngineChannel =
1227     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1228    
1229     if (args->arg(0)->exprType() == INT_EXPR) {
1230     const ScriptID id = args->arg(0)->asInt()->evalInt();
1231     if (!id) {
1232     wrnMsg("change_attack(): note ID for argument 1 may not be zero");
1233     return successResult();
1234     }
1235     if (!id.isNoteID()) {
1236     wrnMsg("change_attack(): argument 1 is not a note ID");
1237     return successResult();
1238     }
1239    
1240     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1241 schoenebeck 2962 if (!pNote) return successResult();
1242 schoenebeck 2953
1243 schoenebeck 2962 // if change_attack() was called immediately after note was triggered
1244     // then immediately apply attack to Note object
1245 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1246 schoenebeck 3561 pNote->Override.Attack.Value = fAttack;
1247     pNote->Override.Attack.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit);
1248 schoenebeck 2962 } else { // otherwise schedule attack change ...
1249     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1250     e.Init(); // clear IDs
1251     e.Type = Event::type_note_synth_param;
1252     e.Param.NoteSynthParam.NoteID = id.noteID();
1253     e.Param.NoteSynthParam.Type = Event::synth_param_attack;
1254     e.Param.NoteSynthParam.Delta = fAttack;
1255 schoenebeck 3561 e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1256     isFinal, false, unit
1257     );
1258 schoenebeck 2962 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1259     }
1260 schoenebeck 2953 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1261     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1262 schoenebeck 3557 for (vmint i = 0; i < ids->arraySize(); ++i) {
1263 schoenebeck 2953 const ScriptID id = ids->evalIntElement(i);
1264     if (!id || !id.isNoteID()) continue;
1265    
1266     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1267     if (!pNote) continue;
1268    
1269 schoenebeck 2962 // if change_attack() was called immediately after note was triggered
1270     // then immediately apply attack to Note object
1271 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1272 schoenebeck 3561 pNote->Override.Attack.Value = fAttack;
1273     pNote->Override.Attack.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit);
1274 schoenebeck 2962 } else { // otherwise schedule attack change ...
1275     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1276     e.Init(); // clear IDs
1277     e.Type = Event::type_note_synth_param;
1278     e.Param.NoteSynthParam.NoteID = id.noteID();
1279     e.Param.NoteSynthParam.Type = Event::synth_param_attack;
1280     e.Param.NoteSynthParam.Delta = fAttack;
1281 schoenebeck 3561 e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1282     isFinal, false, unit
1283     );
1284 schoenebeck 2962 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1285     }
1286 schoenebeck 2953 }
1287     }
1288    
1289     return successResult();
1290     }
1291    
1292     // change_decay() function
1293 schoenebeck 3743 //
1294     //TODO: Derive from generalized, shared template class
1295     // VMChangeSynthParamFunction instead (like e.g. change_cutoff_decay()
1296     // implementation below already does) to ease maintenance.
1297    
1298 schoenebeck 2953 InstrumentScriptVMFunction_change_decay::InstrumentScriptVMFunction_change_decay(InstrumentScriptVM* parent)
1299     : m_vm(parent)
1300     {
1301     }
1302    
1303 schoenebeck 3557 bool InstrumentScriptVMFunction_change_decay::acceptsArgType(vmint iArg, ExprType_t type) const {
1304 schoenebeck 2953 if (iArg == 0)
1305     return type == INT_EXPR || type == INT_ARR_EXPR;
1306     else
1307 schoenebeck 3587 return type == INT_EXPR || type == REAL_EXPR;
1308 schoenebeck 2953 }
1309    
1310 schoenebeck 3561 bool InstrumentScriptVMFunction_change_decay::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
1311     if (iArg == 1)
1312     return type == VM_NO_UNIT || type == VM_SECOND;
1313     else
1314     return type == VM_NO_UNIT;
1315     }
1316    
1317 schoenebeck 3564 bool InstrumentScriptVMFunction_change_decay::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
1318 schoenebeck 3587 return iArg == 1 && type == VM_SECOND; // only allow metric prefix(es) if 'seconds' is used as unit type
1319 schoenebeck 3561 }
1320    
1321     bool InstrumentScriptVMFunction_change_decay::acceptsArgFinal(vmint iArg) const {
1322     return iArg == 1;
1323     }
1324    
1325 schoenebeck 3588 void InstrumentScriptVMFunction_change_decay::checkArgs(VMFnArgs* args,
1326     std::function<void(String)> err,
1327     std::function<void(String)> wrn)
1328     {
1329     // super class checks
1330     Super::checkArgs(args, err, wrn);
1331    
1332     // own checks ...
1333     if (args->argsCount() >= 2) {
1334     VMNumberExpr* argTime = args->arg(1)->asNumber();
1335     if (argTime->unitType() && !argTime->isFinal()) {
1336     wrn("Argument 2 implies 'final' value when using seconds as unit for decay time.");
1337     }
1338     }
1339     }
1340    
1341 schoenebeck 2953 VMFnResult* InstrumentScriptVMFunction_change_decay::exec(VMFnArgs* args) {
1342 schoenebeck 3588 const StdUnit_t unit = args->arg(1)->asNumber()->unitType();
1343 schoenebeck 3587 vmint decay =
1344     (unit) ?
1345     args->arg(1)->asNumber()->evalCastInt(VM_MICRO) :
1346     args->arg(1)->asNumber()->evalCastInt();
1347 schoenebeck 3588 const bool isFinal =
1348     (unit) ?
1349     true : // imply 'final' value if unit type is used
1350     args->arg(1)->asNumber()->isFinal();
1351 schoenebeck 3303 // note: intentionally not checking against a max. value here!
1352     // (to allow i.e. passing 2000000 for doubling the decay time)
1353     if (decay < 0) {
1354 schoenebeck 2953 wrnMsg("change_decay(): argument 2 may not be negative");
1355     decay = 0;
1356     }
1357 schoenebeck 3588 const float fDecay =
1358 schoenebeck 3742 (unit) ?
1359     float(decay) / 1000000.f /* us -> s */ :
1360     float(decay) / float(VM_EG_PAR_MAX_VALUE);
1361 schoenebeck 2953
1362     AbstractEngineChannel* pEngineChannel =
1363     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1364    
1365     if (args->arg(0)->exprType() == INT_EXPR) {
1366     const ScriptID id = args->arg(0)->asInt()->evalInt();
1367     if (!id) {
1368     wrnMsg("change_decay(): note ID for argument 1 may not be zero");
1369     return successResult();
1370     }
1371     if (!id.isNoteID()) {
1372     wrnMsg("change_decay(): argument 1 is not a note ID");
1373     return successResult();
1374     }
1375    
1376     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1377 schoenebeck 2962 if (!pNote) return successResult();
1378 schoenebeck 2953
1379 schoenebeck 2962 // if change_decay() was called immediately after note was triggered
1380     // then immediately apply decay to Note object
1381 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1382 schoenebeck 3561 pNote->Override.Decay.Value = fDecay;
1383     pNote->Override.Decay.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit);
1384 schoenebeck 2962 } else { // otherwise schedule decay change ...
1385     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1386     e.Init(); // clear IDs
1387     e.Type = Event::type_note_synth_param;
1388     e.Param.NoteSynthParam.NoteID = id.noteID();
1389     e.Param.NoteSynthParam.Type = Event::synth_param_decay;
1390     e.Param.NoteSynthParam.Delta = fDecay;
1391 schoenebeck 3561 e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1392     isFinal, false, unit
1393     );
1394 schoenebeck 2962 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1395     }
1396 schoenebeck 2953 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1397     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1398 schoenebeck 3557 for (vmint i = 0; i < ids->arraySize(); ++i) {
1399 schoenebeck 2953 const ScriptID id = ids->evalIntElement(i);
1400     if (!id || !id.isNoteID()) continue;
1401    
1402     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1403     if (!pNote) continue;
1404    
1405 schoenebeck 2962 // if change_decay() was called immediately after note was triggered
1406     // then immediately apply decay to Note object
1407 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1408 schoenebeck 3561 pNote->Override.Decay.Value = fDecay;
1409     pNote->Override.Decay.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit);
1410 schoenebeck 2962 } else { // otherwise schedule decay change ...
1411     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1412     e.Init(); // clear IDs
1413     e.Type = Event::type_note_synth_param;
1414     e.Param.NoteSynthParam.NoteID = id.noteID();
1415     e.Param.NoteSynthParam.Type = Event::synth_param_decay;
1416     e.Param.NoteSynthParam.Delta = fDecay;
1417 schoenebeck 3561 e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1418     isFinal, false, unit
1419     );
1420 schoenebeck 2962 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1421     }
1422 schoenebeck 2953 }
1423     }
1424    
1425     return successResult();
1426     }
1427    
1428     // change_release() function
1429 schoenebeck 3743 //
1430     //TODO: Derive from generalized, shared template class
1431     // VMChangeSynthParamFunction instead (like e.g. change_cutoff_release()
1432     // implementation below already does) to ease maintenance.
1433    
1434 schoenebeck 2953 InstrumentScriptVMFunction_change_release::InstrumentScriptVMFunction_change_release(InstrumentScriptVM* parent)
1435     : m_vm(parent)
1436     {
1437     }
1438    
1439 schoenebeck 3557 bool InstrumentScriptVMFunction_change_release::acceptsArgType(vmint iArg, ExprType_t type) const {
1440 schoenebeck 2953 if (iArg == 0)
1441     return type == INT_EXPR || type == INT_ARR_EXPR;
1442     else
1443 schoenebeck 3587 return type == INT_EXPR || type == REAL_EXPR;
1444 schoenebeck 2953 }
1445    
1446 schoenebeck 3561 bool InstrumentScriptVMFunction_change_release::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
1447     if (iArg == 1)
1448     return type == VM_NO_UNIT || type == VM_SECOND;
1449     else
1450     return type == VM_NO_UNIT;
1451     }
1452    
1453 schoenebeck 3564 bool InstrumentScriptVMFunction_change_release::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
1454 schoenebeck 3587 return iArg == 1 && type == VM_SECOND; // only allow metric prefix(es) if 'seconds' is used as unit type
1455 schoenebeck 3561 }
1456    
1457     bool InstrumentScriptVMFunction_change_release::acceptsArgFinal(vmint iArg) const {
1458     return iArg == 1;
1459     }
1460    
1461 schoenebeck 3588 void InstrumentScriptVMFunction_change_release::checkArgs(VMFnArgs* args,
1462     std::function<void(String)> err,
1463     std::function<void(String)> wrn)
1464     {
1465     // super class checks
1466     Super::checkArgs(args, err, wrn);
1467    
1468     // own checks ...
1469     if (args->argsCount() >= 2) {
1470     VMNumberExpr* argTime = args->arg(1)->asNumber();
1471     if (argTime->unitType() && !argTime->isFinal()) {
1472     wrn("Argument 2 implies 'final' value when using seconds as unit for release time.");
1473     }
1474     }
1475     }
1476    
1477 schoenebeck 2953 VMFnResult* InstrumentScriptVMFunction_change_release::exec(VMFnArgs* args) {
1478 schoenebeck 3588 const StdUnit_t unit = args->arg(1)->asNumber()->unitType();
1479 schoenebeck 3587 vmint release =
1480     (unit) ?
1481     args->arg(1)->asNumber()->evalCastInt(VM_MICRO) :
1482     args->arg(1)->asNumber()->evalCastInt();
1483 schoenebeck 3588 const bool isFinal =
1484     (unit) ?
1485     true : // imply 'final' value if unit type is used
1486     args->arg(1)->asNumber()->isFinal();
1487 schoenebeck 3303 // note: intentionally not checking against a max. value here!
1488     // (to allow i.e. passing 2000000 for doubling the release time)
1489     if (release < 0) {
1490 schoenebeck 2953 wrnMsg("change_release(): argument 2 may not be negative");
1491     release = 0;
1492     }
1493 schoenebeck 3588 const float fRelease =
1494 schoenebeck 3742 (unit) ?
1495     float(release) / 1000000.f /* us -> s */ :
1496     float(release) / float(VM_EG_PAR_MAX_VALUE);
1497 schoenebeck 2953
1498     AbstractEngineChannel* pEngineChannel =
1499     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1500    
1501     if (args->arg(0)->exprType() == INT_EXPR) {
1502     const ScriptID id = args->arg(0)->asInt()->evalInt();
1503     if (!id) {
1504     wrnMsg("change_release(): note ID for argument 1 may not be zero");
1505     return successResult();
1506     }
1507     if (!id.isNoteID()) {
1508     wrnMsg("change_release(): argument 1 is not a note ID");
1509     return successResult();
1510     }
1511    
1512     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1513 schoenebeck 2962 if (!pNote) return successResult();
1514 schoenebeck 2953
1515 schoenebeck 2962 // if change_release() was called immediately after note was triggered
1516     // then immediately apply relase to Note object
1517 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1518 schoenebeck 3561 pNote->Override.Release.Value = fRelease;
1519     pNote->Override.Release.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit);
1520 schoenebeck 2962 } else { // otherwise schedule release change ...
1521     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1522     e.Init(); // clear IDs
1523     e.Type = Event::type_note_synth_param;
1524     e.Param.NoteSynthParam.NoteID = id.noteID();
1525     e.Param.NoteSynthParam.Type = Event::synth_param_release;
1526     e.Param.NoteSynthParam.Delta = fRelease;
1527 schoenebeck 3561 e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1528     isFinal, false, unit
1529     );
1530 schoenebeck 2962 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1531     }
1532 schoenebeck 2953 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1533     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1534 schoenebeck 3557 for (vmint i = 0; i < ids->arraySize(); ++i) {
1535 schoenebeck 2953 const ScriptID id = ids->evalIntElement(i);
1536     if (!id || !id.isNoteID()) continue;
1537    
1538     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1539     if (!pNote) continue;
1540    
1541 schoenebeck 2962 // if change_release() was called immediately after note was triggered
1542     // then immediately apply relase to Note object
1543 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1544 schoenebeck 3561 pNote->Override.Release.Value = fRelease;
1545     pNote->Override.Release.Scope = NoteBase::scopeBy_FinalUnit(isFinal, unit);
1546 schoenebeck 2962 } else { // otherwise schedule release change ...
1547     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1548     e.Init(); // clear IDs
1549     e.Type = Event::type_note_synth_param;
1550     e.Param.NoteSynthParam.NoteID = id.noteID();
1551     e.Param.NoteSynthParam.Type = Event::synth_param_release;
1552     e.Param.NoteSynthParam.Delta = fRelease;
1553 schoenebeck 3561 e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1554     isFinal, false, unit
1555     );
1556 schoenebeck 2962 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1557     }
1558 schoenebeck 2953 }
1559     }
1560    
1561     return successResult();
1562     }
1563    
1564 schoenebeck 3246 // template for change_*() functions
1565    
1566 schoenebeck 3557 bool VMChangeSynthParamFunction::acceptsArgType(vmint iArg, ExprType_t type) const {
1567 schoenebeck 3118 if (iArg == 0)
1568     return type == INT_EXPR || type == INT_ARR_EXPR;
1569     else
1570 schoenebeck 3587 return type == INT_EXPR || (m_acceptReal && type == REAL_EXPR);
1571 schoenebeck 3118 }
1572    
1573 schoenebeck 3561 bool VMChangeSynthParamFunction::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
1574     if (iArg == 1)
1575     return type == VM_NO_UNIT || type == m_unit;
1576     else
1577     return type == VM_NO_UNIT;
1578     }
1579    
1580 schoenebeck 3564 bool VMChangeSynthParamFunction::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
1581 schoenebeck 3587 return m_acceptUnitPrefix && iArg == 1 && type == m_unit; // only allow metric prefix(es) if approprirate unit type is used (e.g. Hz)
1582 schoenebeck 3561 }
1583    
1584     bool VMChangeSynthParamFunction::acceptsArgFinal(vmint iArg) const {
1585     return (m_acceptFinal) ? (iArg == 1) : false;
1586     }
1587    
1588     inline static void setNoteParamScopeBy_FinalUnit(NoteBase::Param& param, const bool bFinal, const StdUnit_t unit) {
1589     param.Scope = NoteBase::scopeBy_FinalUnit(bFinal, unit);
1590     }
1591    
1592     inline static void setNoteParamScopeBy_FinalUnit(NoteBase::Norm& param, const bool bFinal, const StdUnit_t unit) {
1593     param.Final = bFinal;
1594     }
1595    
1596     inline static void setNoteParamScopeBy_FinalUnit(float& param, const bool bFinal, const StdUnit_t unit) {
1597     /* NOOP */
1598     }
1599    
1600     template<class T>
1601     inline static void setNoteParamValue(T& param, vmfloat value) {
1602     param.Value = value;
1603     }
1604    
1605     inline static void setNoteParamValue(float& param, vmfloat value) {
1606     param = value;
1607     }
1608    
1609 schoenebeck 3588 void VMChangeSynthParamFunction::checkArgs(VMFnArgs* args,
1610     std::function<void(String)> err,
1611     std::function<void(String)> wrn)
1612     {
1613     // super class checks
1614     Super::checkArgs(args, err, wrn);
1615    
1616     // own checks ...
1617     if (m_unit && m_unit != VM_BEL && args->argsCount() >= 2) {
1618     VMNumberExpr* arg = args->arg(1)->asNumber();
1619     if (arg && arg->unitType() && !arg->isFinal()) {
1620     wrn("Argument 2 implies 'final' value when unit type " +
1621     unitTypeStr(arg->unitType()) + " is used.");
1622     }
1623     }
1624     }
1625    
1626 schoenebeck 3193 // Arbitrarily chosen constant value symbolizing "no limit".
1627     #define NO_LIMIT 1315916909
1628    
1629 schoenebeck 3561 template<class T_NoteParamType, T_NoteParamType NoteBase::_Override::*T_noteParam,
1630     vmint T_synthParam,
1631     vmint T_minValueNorm, vmint T_maxValueNorm, bool T_normalizeNorm,
1632     vmint T_minValueUnit, vmint T_maxValueUnit,
1633     MetricPrefix_t T_unitPrefix0, MetricPrefix_t ... T_unitPrefixN>
1634     VMFnResult* VMChangeSynthParamFunction::execTemplate(VMFnArgs* args, const char* functionName)
1635     {
1636 schoenebeck 3587 const StdUnit_t unit = args->arg(1)->asNumber()->unitType();
1637 schoenebeck 3588 const bool isFinal =
1638     (m_unit && m_unit != VM_BEL && unit) ?
1639     true : // imply 'final' value if unit type is used (except of 'Bel' which may be relative)
1640     args->arg(1)->asNumber()->isFinal();
1641 schoenebeck 3561 vmint value =
1642 schoenebeck 3587 (m_acceptUnitPrefix && ((m_unit && unit) || (!m_unit && args->arg(1)->asNumber()->hasUnitFactorNow())))
1643     ? args->arg(1)->asNumber()->evalCastInt(T_unitPrefix0, T_unitPrefixN ...)
1644     : args->arg(1)->asNumber()->evalCastInt();
1645 schoenebeck 3561
1646     // check if passed value is in allowed range
1647     if (unit && m_unit) {
1648     if (T_maxValueUnit != NO_LIMIT && value > T_maxValueUnit) {
1649     wrnMsg(String(functionName) + "(): argument 2 may not be larger than " + ToString(T_maxValueUnit));
1650     value = T_maxValueUnit;
1651     } else if (T_minValueUnit != NO_LIMIT && value < T_minValueUnit) {
1652     if (T_minValueUnit == 0)
1653     wrnMsg(String(functionName) + "(): argument 2 may not be negative");
1654     else
1655     wrnMsg(String(functionName) + "(): argument 2 may not be smaller than " + ToString(T_minValueUnit));
1656     value = T_minValueUnit;
1657     }
1658     } else { // value was passed to this function without a unit ...
1659     if (T_maxValueNorm != NO_LIMIT && value > T_maxValueNorm) {
1660     wrnMsg(String(functionName) + "(): argument 2 may not be larger than " + ToString(T_maxValueNorm));
1661     value = T_maxValueNorm;
1662     } else if (T_minValueNorm != NO_LIMIT && value < T_minValueNorm) {
1663     if (T_minValueNorm == 0)
1664     wrnMsg(String(functionName) + "(): argument 2 may not be negative");
1665     else
1666     wrnMsg(String(functionName) + "(): argument 2 may not be smaller than " + ToString(T_minValueNorm));
1667     value = T_minValueNorm;
1668     }
1669     }
1670    
1671     // convert passed argument value to engine internal expected value range (i.e. 0.0 .. 1.0)
1672     const float fValue =
1673     (unit && m_unit) ?
1674     (unit == VM_BEL) ?
1675     RTMath::DecibelToLinRatio(float(value) * float(T_unitPrefix0) /*i.e. mdB -> dB*/) :
1676     float(value) * VMUnit::unitFactor(T_unitPrefix0, T_unitPrefixN ...) /*i.e. us -> s*/ :
1677     (T_normalizeNorm) ?
1678     float(value) / ((T_maxValueNorm != NO_LIMIT) ? float(T_maxValueNorm) : 1000000.f/* fallback: value range used for most */) :
1679     float(value) /* as is */;
1680    
1681 schoenebeck 3118 AbstractEngineChannel* pEngineChannel =
1682     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1683    
1684     if (args->arg(0)->exprType() == INT_EXPR) {
1685     const ScriptID id = args->arg(0)->asInt()->evalInt();
1686     if (!id) {
1687     wrnMsg(String(functionName) + "(): note ID for argument 1 may not be zero");
1688     return successResult();
1689     }
1690     if (!id.isNoteID()) {
1691     wrnMsg(String(functionName) + "(): argument 1 is not a note ID");
1692     return successResult();
1693     }
1694    
1695     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1696     if (!pNote) return successResult();
1697    
1698     // if this change_*() script function was called immediately after
1699     // note was triggered then immediately apply the synth parameter
1700     // change to Note object
1701 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1702 schoenebeck 3561 setNoteParamValue(pNote->Override.*T_noteParam, fValue);
1703     setNoteParamScopeBy_FinalUnit(
1704     (pNote->Override.*T_noteParam),
1705     isFinal, unit
1706     );
1707 schoenebeck 3118 } else { // otherwise schedule this synth parameter change ...
1708     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1709     e.Init(); // clear IDs
1710     e.Type = Event::type_note_synth_param;
1711     e.Param.NoteSynthParam.NoteID = id.noteID();
1712     e.Param.NoteSynthParam.Type = (Event::synth_param_t) T_synthParam;
1713     e.Param.NoteSynthParam.Delta = fValue;
1714 schoenebeck 3561 e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1715     isFinal, false, unit
1716     );
1717 schoenebeck 3118 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1718     }
1719     } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1720     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1721 schoenebeck 3557 for (vmint i = 0; i < ids->arraySize(); ++i) {
1722 schoenebeck 3118 const ScriptID id = ids->evalIntElement(i);
1723     if (!id || !id.isNoteID()) continue;
1724    
1725     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1726     if (!pNote) continue;
1727    
1728     // if this change_*() script function was called immediately after
1729     // note was triggered then immediately apply the synth parameter
1730     // change to Note object
1731 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1732 schoenebeck 3561 setNoteParamValue(pNote->Override.*T_noteParam, fValue);
1733     setNoteParamScopeBy_FinalUnit(
1734     (pNote->Override.*T_noteParam),
1735     isFinal, unit
1736     );
1737 schoenebeck 3118 } else { // otherwise schedule this synth parameter change ...
1738     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1739     e.Init(); // clear IDs
1740     e.Type = Event::type_note_synth_param;
1741     e.Param.NoteSynthParam.NoteID = id.noteID();
1742     e.Param.NoteSynthParam.Type = (Event::synth_param_t) T_synthParam;
1743     e.Param.NoteSynthParam.Delta = fValue;
1744 schoenebeck 3561 e.Param.NoteSynthParam.Scope = Event::scopeBy_FinalRelativeUnit(
1745     isFinal, false, unit
1746     );
1747 schoenebeck 3118 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1748     }
1749     }
1750     }
1751    
1752     return successResult();
1753     }
1754    
1755 schoenebeck 3316 // change_sustain() function
1756    
1757     VMFnResult* InstrumentScriptVMFunction_change_sustain::exec(VMFnArgs* args) {
1758     return VMChangeSynthParamFunction::execTemplate<
1759 schoenebeck 3561 decltype(NoteBase::_Override::Sustain),
1760 schoenebeck 3360 &NoteBase::_Override::Sustain,
1761     Event::synth_param_sustain,
1762 schoenebeck 3561 /* if value passed without unit */
1763     0, NO_LIMIT, true,
1764     /* if value passed WITH 'Bel' unit */
1765     NO_LIMIT, NO_LIMIT, VM_MILLI, VM_DECI>( args, "change_sustain" );
1766 schoenebeck 3316 }
1767    
1768 schoenebeck 3360 // change_cutoff_attack() function
1769    
1770     VMFnResult* InstrumentScriptVMFunction_change_cutoff_attack::exec(VMFnArgs* args) {
1771     return VMChangeSynthParamFunction::execTemplate<
1772 schoenebeck 3561 decltype(NoteBase::_Override::CutoffAttack),
1773 schoenebeck 3360 &NoteBase::_Override::CutoffAttack,
1774     Event::synth_param_cutoff_attack,
1775 schoenebeck 3561 /* if value passed without unit */
1776     0, NO_LIMIT, true,
1777     /* if value passed with 'seconds' unit */
1778     0, NO_LIMIT, VM_MICRO>( args, "change_cutoff_attack" );
1779 schoenebeck 3360 }
1780    
1781     // change_cutoff_decay() function
1782    
1783     VMFnResult* InstrumentScriptVMFunction_change_cutoff_decay::exec(VMFnArgs* args) {
1784     return VMChangeSynthParamFunction::execTemplate<
1785 schoenebeck 3561 decltype(NoteBase::_Override::CutoffDecay),
1786 schoenebeck 3360 &NoteBase::_Override::CutoffDecay,
1787     Event::synth_param_cutoff_decay,
1788 schoenebeck 3561 /* if value passed without unit */
1789     0, NO_LIMIT, true,
1790     /* if value passed with 'seconds' unit */
1791     0, NO_LIMIT, VM_MICRO>( args, "change_cutoff_decay" );
1792 schoenebeck 3360 }
1793    
1794     // change_cutoff_sustain() function
1795    
1796     VMFnResult* InstrumentScriptVMFunction_change_cutoff_sustain::exec(VMFnArgs* args) {
1797     return VMChangeSynthParamFunction::execTemplate<
1798 schoenebeck 3561 decltype(NoteBase::_Override::CutoffSustain),
1799 schoenebeck 3360 &NoteBase::_Override::CutoffSustain,
1800     Event::synth_param_cutoff_sustain,
1801 schoenebeck 3561 /* if value passed without unit */
1802     0, NO_LIMIT, true,
1803     /* if value passed WITH 'Bel' unit */
1804     NO_LIMIT, NO_LIMIT, VM_MILLI, VM_DECI>( args, "change_cutoff_sustain" );
1805 schoenebeck 3360 }
1806    
1807     // change_cutoff_release() function
1808    
1809     VMFnResult* InstrumentScriptVMFunction_change_cutoff_release::exec(VMFnArgs* args) {
1810     return VMChangeSynthParamFunction::execTemplate<
1811 schoenebeck 3561 decltype(NoteBase::_Override::CutoffRelease),
1812 schoenebeck 3360 &NoteBase::_Override::CutoffRelease,
1813     Event::synth_param_cutoff_release,
1814 schoenebeck 3561 /* if value passed without unit */
1815     0, NO_LIMIT, true,
1816     /* if value passed with 'seconds' unit */
1817     0, NO_LIMIT, VM_MICRO>( args, "change_cutoff_release" );
1818 schoenebeck 3360 }
1819    
1820 schoenebeck 3118 // change_amp_lfo_depth() function
1821    
1822     VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_depth::exec(VMFnArgs* args) {
1823 schoenebeck 3188 return VMChangeSynthParamFunction::execTemplate<
1824 schoenebeck 3561 decltype(NoteBase::_Override::AmpLFODepth),
1825 schoenebeck 3188 &NoteBase::_Override::AmpLFODepth,
1826 schoenebeck 3193 Event::synth_param_amp_lfo_depth,
1827 schoenebeck 3561 /* if value passed without unit */
1828 schoenebeck 3715 0, NO_LIMIT, true,
1829 schoenebeck 3561 /* not used (since this function does not accept unit) */
1830     NO_LIMIT, NO_LIMIT, VM_NO_PREFIX>( args, "change_amp_lfo_depth" );
1831 schoenebeck 3118 }
1832    
1833     // change_amp_lfo_freq() function
1834    
1835     VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_freq::exec(VMFnArgs* args) {
1836 schoenebeck 3188 return VMChangeSynthParamFunction::execTemplate<
1837 schoenebeck 3561 decltype(NoteBase::_Override::AmpLFOFreq),
1838 schoenebeck 3188 &NoteBase::_Override::AmpLFOFreq,
1839 schoenebeck 3193 Event::synth_param_amp_lfo_freq,
1840 schoenebeck 3561 /* if value passed without unit */
1841 schoenebeck 3715 0, NO_LIMIT, true,
1842 schoenebeck 3561 /* if value passed with 'Hz' unit */
1843     0, 30000, VM_NO_PREFIX>( args, "change_amp_lfo_freq" );
1844 schoenebeck 3118 }
1845    
1846 schoenebeck 3360 // change_cutoff_lfo_depth() function
1847    
1848     VMFnResult* InstrumentScriptVMFunction_change_cutoff_lfo_depth::exec(VMFnArgs* args) {
1849     return VMChangeSynthParamFunction::execTemplate<
1850 schoenebeck 3561 decltype(NoteBase::_Override::CutoffLFODepth),
1851 schoenebeck 3360 &NoteBase::_Override::CutoffLFODepth,
1852     Event::synth_param_cutoff_lfo_depth,
1853 schoenebeck 3561 /* if value passed without unit */
1854 schoenebeck 3715 0, NO_LIMIT, true,
1855 schoenebeck 3561 /* not used (since this function does not accept unit) */
1856     NO_LIMIT, NO_LIMIT, VM_NO_PREFIX>( args, "change_cutoff_lfo_depth" );
1857 schoenebeck 3360 }
1858    
1859     // change_cutoff_lfo_freq() function
1860    
1861     VMFnResult* InstrumentScriptVMFunction_change_cutoff_lfo_freq::exec(VMFnArgs* args) {
1862     return VMChangeSynthParamFunction::execTemplate<
1863 schoenebeck 3561 decltype(NoteBase::_Override::CutoffLFOFreq),
1864 schoenebeck 3360 &NoteBase::_Override::CutoffLFOFreq,
1865     Event::synth_param_cutoff_lfo_freq,
1866 schoenebeck 3561 /* if value passed without unit */
1867 schoenebeck 3715 0, NO_LIMIT, true,
1868 schoenebeck 3561 /* if value passed with 'Hz' unit */
1869     0, 30000, VM_NO_PREFIX>( args, "change_cutoff_lfo_freq" );
1870 schoenebeck 3360 }
1871    
1872 schoenebeck 3118 // change_pitch_lfo_depth() function
1873    
1874     VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_depth::exec(VMFnArgs* args) {
1875 schoenebeck 3188 return VMChangeSynthParamFunction::execTemplate<
1876 schoenebeck 3561 decltype(NoteBase::_Override::PitchLFODepth),
1877 schoenebeck 3188 &NoteBase::_Override::PitchLFODepth,
1878 schoenebeck 3193 Event::synth_param_pitch_lfo_depth,
1879 schoenebeck 3561 /* if value passed without unit */
1880 schoenebeck 3715 0, NO_LIMIT, true,
1881 schoenebeck 3561 /* not used (since this function does not accept unit) */
1882     NO_LIMIT, NO_LIMIT, VM_NO_PREFIX>( args, "change_pitch_lfo_depth" );
1883 schoenebeck 3118 }
1884    
1885     // change_pitch_lfo_freq() function
1886    
1887     VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_freq::exec(VMFnArgs* args) {
1888 schoenebeck 3188 return VMChangeSynthParamFunction::execTemplate<
1889 schoenebeck 3561 decltype(NoteBase::_Override::PitchLFOFreq),
1890 schoenebeck 3188 &NoteBase::_Override::PitchLFOFreq,
1891 schoenebeck 3193 Event::synth_param_pitch_lfo_freq,
1892 schoenebeck 3561 /* if value passed without unit */
1893 schoenebeck 3715 0, NO_LIMIT, true,
1894 schoenebeck 3561 /* if value passed with 'Hz' unit */
1895     0, 30000, VM_NO_PREFIX>( args, "change_pitch_lfo_freq" );
1896 schoenebeck 3118 }
1897    
1898 schoenebeck 3188 // change_vol_time() function
1899    
1900     VMFnResult* InstrumentScriptVMFunction_change_vol_time::exec(VMFnArgs* args) {
1901     return VMChangeSynthParamFunction::execTemplate<
1902 schoenebeck 3561 decltype(NoteBase::_Override::VolumeTime),
1903 schoenebeck 3188 &NoteBase::_Override::VolumeTime,
1904 schoenebeck 3193 Event::synth_param_volume_time,
1905 schoenebeck 3561 /* if value passed without unit (implying 'us' unit) */
1906     0, NO_LIMIT, true,
1907     /* if value passed with 'seconds' unit */
1908     0, NO_LIMIT, VM_MICRO>( args, "change_vol_time" );
1909 schoenebeck 3188 }
1910    
1911     // change_tune_time() function
1912    
1913     VMFnResult* InstrumentScriptVMFunction_change_tune_time::exec(VMFnArgs* args) {
1914     return VMChangeSynthParamFunction::execTemplate<
1915 schoenebeck 3561 decltype(NoteBase::_Override::PitchTime),
1916 schoenebeck 3188 &NoteBase::_Override::PitchTime,
1917 schoenebeck 3193 Event::synth_param_pitch_time,
1918 schoenebeck 3561 /* if value passed without unit (implying 'us' unit) */
1919     0, NO_LIMIT, true,
1920     /* if value passed with 'seconds' unit */
1921     0, NO_LIMIT, VM_MICRO>( args, "change_tune_time" );
1922 schoenebeck 3188 }
1923    
1924 schoenebeck 3335 // change_pan_time() function
1925    
1926     VMFnResult* InstrumentScriptVMFunction_change_pan_time::exec(VMFnArgs* args) {
1927     return VMChangeSynthParamFunction::execTemplate<
1928 schoenebeck 3561 decltype(NoteBase::_Override::PanTime),
1929     &NoteBase::_Override::PanTime,
1930     Event::synth_param_pan_time,
1931     /* if value passed without unit (implying 'us' unit) */
1932     0, NO_LIMIT, true,
1933     /* if value passed with 'seconds' unit */
1934     0, NO_LIMIT, VM_MICRO>( args, "change_pan_time" );
1935 schoenebeck 3335 }
1936    
1937 schoenebeck 3246 // template for change_*_curve() functions
1938    
1939 schoenebeck 3557 bool VMChangeFadeCurveFunction::acceptsArgType(vmint iArg, ExprType_t type) const {
1940 schoenebeck 3246 if (iArg == 0)
1941     return type == INT_EXPR || type == INT_ARR_EXPR;
1942     else
1943     return type == INT_EXPR;
1944     }
1945    
1946 schoenebeck 3557 template<fade_curve_t NoteBase::_Override::*T_noteParam, vmint T_synthParam>
1947 schoenebeck 3246 VMFnResult* VMChangeFadeCurveFunction::execTemplate(VMFnArgs* args, const char* functionName) {
1948 schoenebeck 3557 vmint value = args->arg(1)->asInt()->evalInt();
1949 schoenebeck 3246 switch (value) {
1950     case FADE_CURVE_LINEAR:
1951     case FADE_CURVE_EASE_IN_EASE_OUT:
1952     break;
1953     default:
1954     wrnMsg(String(functionName) + "(): invalid curve type passed as argument 2");
1955     return successResult();
1956     }
1957    
1958     AbstractEngineChannel* pEngineChannel =
1959     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1960    
1961     if (args->arg(0)->exprType() == INT_EXPR) {
1962     const ScriptID id = args->arg(0)->asInt()->evalInt();
1963     if (!id) {
1964     wrnMsg(String(functionName) + "(): note ID for argument 1 may not be zero");
1965     return successResult();
1966     }
1967     if (!id.isNoteID()) {
1968     wrnMsg(String(functionName) + "(): argument 1 is not a note ID");
1969     return successResult();
1970     }
1971    
1972     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1973     if (!pNote) return successResult();
1974    
1975     // if this change_*_curve() script function was called immediately after
1976     // note was triggered then immediately apply the synth parameter
1977     // change to Note object
1978     if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1979     pNote->Override.*T_noteParam = (fade_curve_t) value;
1980     } else { // otherwise schedule this synth parameter change ...
1981     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1982     e.Init(); // clear IDs
1983     e.Type = Event::type_note_synth_param;
1984     e.Param.NoteSynthParam.NoteID = id.noteID();
1985     e.Param.NoteSynthParam.Type = (Event::synth_param_t) T_synthParam;
1986     e.Param.NoteSynthParam.Delta = value;
1987 schoenebeck 3561 e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
1988 schoenebeck 3246
1989     pEngineChannel->ScheduleEventMicroSec(&e, 0);
1990     }
1991     } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1992     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1993 schoenebeck 3557 for (vmint i = 0; i < ids->arraySize(); ++i) {
1994 schoenebeck 3246 const ScriptID id = ids->evalIntElement(i);
1995     if (!id || !id.isNoteID()) continue;
1996    
1997     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1998     if (!pNote) continue;
1999    
2000     // if this change_*_curve() script function was called immediately after
2001     // note was triggered then immediately apply the synth parameter
2002     // change to Note object
2003     if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
2004     pNote->Override.*T_noteParam = (fade_curve_t) value;
2005     } else { // otherwise schedule this synth parameter change ...
2006     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
2007     e.Init(); // clear IDs
2008     e.Type = Event::type_note_synth_param;
2009     e.Param.NoteSynthParam.NoteID = id.noteID();
2010     e.Param.NoteSynthParam.Type = (Event::synth_param_t) T_synthParam;
2011     e.Param.NoteSynthParam.Delta = value;
2012 schoenebeck 3561 e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
2013 schoenebeck 3246
2014     pEngineChannel->ScheduleEventMicroSec(&e, 0);
2015     }
2016     }
2017     }
2018    
2019     return successResult();
2020     }
2021    
2022     // change_vol_curve() function
2023    
2024     VMFnResult* InstrumentScriptVMFunction_change_vol_curve::exec(VMFnArgs* args) {
2025     return VMChangeFadeCurveFunction::execTemplate<
2026     &NoteBase::_Override::VolumeCurve,
2027     Event::synth_param_volume_curve>( args, "change_vol_curve" );
2028     }
2029    
2030     // change_tune_curve() function
2031    
2032     VMFnResult* InstrumentScriptVMFunction_change_tune_curve::exec(VMFnArgs* args) {
2033     return VMChangeFadeCurveFunction::execTemplate<
2034     &NoteBase::_Override::PitchCurve,
2035     Event::synth_param_pitch_curve>( args, "change_tune_curve" );
2036     }
2037    
2038 schoenebeck 3335 // change_pan_curve() function
2039    
2040     VMFnResult* InstrumentScriptVMFunction_change_pan_curve::exec(VMFnArgs* args) {
2041     return VMChangeFadeCurveFunction::execTemplate<
2042     &NoteBase::_Override::PanCurve,
2043     Event::synth_param_pan_curve>( args, "change_pan_curve" );
2044     }
2045    
2046 schoenebeck 3188 // fade_in() function
2047    
2048     InstrumentScriptVMFunction_fade_in::InstrumentScriptVMFunction_fade_in(InstrumentScriptVM* parent)
2049     : m_vm(parent)
2050     {
2051     }
2052    
2053 schoenebeck 3557 bool InstrumentScriptVMFunction_fade_in::acceptsArgType(vmint iArg, ExprType_t type) const {
2054 schoenebeck 3188 if (iArg == 0)
2055     return type == INT_EXPR || type == INT_ARR_EXPR;
2056     else
2057 schoenebeck 3587 return type == INT_EXPR || type == REAL_EXPR;
2058 schoenebeck 3188 }
2059    
2060 schoenebeck 3561 bool InstrumentScriptVMFunction_fade_in::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
2061     if (iArg == 1)
2062     return type == VM_NO_UNIT || type == VM_SECOND;
2063     else
2064     return type == VM_NO_UNIT;
2065     }
2066    
2067 schoenebeck 3564 bool InstrumentScriptVMFunction_fade_in::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
2068 schoenebeck 3587 return iArg == 1 && type == VM_SECOND; // only allow metric prefix(es) if 'seconds' is used as unit type
2069 schoenebeck 3561 }
2070    
2071 schoenebeck 3188 VMFnResult* InstrumentScriptVMFunction_fade_in::exec(VMFnArgs* args) {
2072 schoenebeck 3587 StdUnit_t unit = args->arg(1)->asNumber()->unitType();
2073     vmint duration =
2074     (unit) ?
2075     args->arg(1)->asNumber()->evalCastInt(VM_MICRO) :
2076     args->arg(1)->asNumber()->evalCastInt();
2077 schoenebeck 3188 if (duration < 0) {
2078     wrnMsg("fade_in(): argument 2 may not be negative");
2079     duration = 0;
2080     }
2081     const float fDuration = float(duration) / 1000000.f; // convert microseconds to seconds
2082    
2083     AbstractEngineChannel* pEngineChannel =
2084     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
2085    
2086     if (args->arg(0)->exprType() == INT_EXPR) {
2087     const ScriptID id = args->arg(0)->asInt()->evalInt();
2088     if (!id) {
2089     wrnMsg("fade_in(): note ID for argument 1 may not be zero");
2090     return successResult();
2091     }
2092     if (!id.isNoteID()) {
2093     wrnMsg("fade_in(): argument 1 is not a note ID");
2094     return successResult();
2095     }
2096    
2097     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
2098     if (!pNote) return successResult();
2099    
2100     // if fade_in() was called immediately after note was triggered
2101     // then immediately apply a start volume of zero to Note object,
2102     // as well as the fade in duration
2103 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
2104 schoenebeck 3561 pNote->Override.Volume.Value = 0.f;
2105 schoenebeck 3188 pNote->Override.VolumeTime = fDuration;
2106     } else { // otherwise schedule a "volume time" change with the requested fade in duration ...
2107     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
2108     e.Init(); // clear IDs
2109     e.Type = Event::type_note_synth_param;
2110     e.Param.NoteSynthParam.NoteID = id.noteID();
2111     e.Param.NoteSynthParam.Type = Event::synth_param_volume_time;
2112     e.Param.NoteSynthParam.Delta = fDuration;
2113 schoenebeck 3561 e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
2114 schoenebeck 3188
2115     pEngineChannel->ScheduleEventMicroSec(&e, 0);
2116     }
2117     // and finally schedule a "volume" change, simply one time slice
2118     // ahead, with the final fade in volume (1.0)
2119     {
2120     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
2121     e.Init(); // clear IDs
2122     e.Type = Event::type_note_synth_param;
2123     e.Param.NoteSynthParam.NoteID = id.noteID();
2124     e.Param.NoteSynthParam.Type = Event::synth_param_volume;
2125     e.Param.NoteSynthParam.Delta = 1.f;
2126 schoenebeck 3561 e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
2127 schoenebeck 3188
2128     // scheduling with 0 delay would also work here, but +1 is more
2129     // safe regarding potential future implementation changes of the
2130     // scheduler (see API comments of RTAVLTree::insert())
2131     pEngineChannel->ScheduleEventMicroSec(&e, 1);
2132     }
2133     } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
2134     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
2135 schoenebeck 3557 for (vmint i = 0; i < ids->arraySize(); ++i) {
2136 schoenebeck 3188 const ScriptID id = ids->evalIntElement(i);
2137     if (!id || !id.isNoteID()) continue;
2138    
2139     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
2140     if (!pNote) continue;
2141    
2142     // if fade_in() was called immediately after note was triggered
2143     // then immediately apply a start volume of zero to Note object,
2144     // as well as the fade in duration
2145 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
2146 schoenebeck 3561 pNote->Override.Volume.Value = 0.f;
2147 schoenebeck 3188 pNote->Override.VolumeTime = fDuration;
2148     } else { // otherwise schedule a "volume time" change with the requested fade in duration ...
2149     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
2150     e.Init(); // clear IDs
2151     e.Type = Event::type_note_synth_param;
2152     e.Param.NoteSynthParam.NoteID = id.noteID();
2153     e.Param.NoteSynthParam.Type = Event::synth_param_volume_time;
2154     e.Param.NoteSynthParam.Delta = fDuration;
2155 schoenebeck 3561 e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
2156 schoenebeck 3188
2157     pEngineChannel->ScheduleEventMicroSec(&e, 0);
2158     }
2159     // and finally schedule a "volume" change, simply one time slice
2160     // ahead, with the final fade in volume (1.0)
2161     {
2162     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
2163     e.Init(); // clear IDs
2164     e.Type = Event::type_note_synth_param;
2165     e.Param.NoteSynthParam.NoteID = id.noteID();
2166     e.Param.NoteSynthParam.Type = Event::synth_param_volume;
2167     e.Param.NoteSynthParam.Delta = 1.f;
2168 schoenebeck 3561 e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
2169 schoenebeck 3188
2170     // scheduling with 0 delay would also work here, but +1 is more
2171     // safe regarding potential future implementation changes of the
2172     // scheduler (see API comments of RTAVLTree::insert())
2173     pEngineChannel->ScheduleEventMicroSec(&e, 1);
2174     }
2175     }
2176     }
2177    
2178     return successResult();
2179     }
2180    
2181     // fade_out() function
2182    
2183     InstrumentScriptVMFunction_fade_out::InstrumentScriptVMFunction_fade_out(InstrumentScriptVM* parent)
2184     : m_vm(parent)
2185     {
2186     }
2187    
2188 schoenebeck 3557 bool InstrumentScriptVMFunction_fade_out::acceptsArgType(vmint iArg, ExprType_t type) const {
2189 schoenebeck 3188 if (iArg == 0)
2190     return type == INT_EXPR || type == INT_ARR_EXPR;
2191     else
2192 schoenebeck 3587 return type == INT_EXPR || type == REAL_EXPR;
2193 schoenebeck 3188 }
2194    
2195 schoenebeck 3561 bool InstrumentScriptVMFunction_fade_out::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
2196     if (iArg == 1)
2197     return type == VM_NO_UNIT || type == VM_SECOND;
2198     else
2199     return type == VM_NO_UNIT;
2200     }
2201    
2202 schoenebeck 3564 bool InstrumentScriptVMFunction_fade_out::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
2203 schoenebeck 3587 return iArg == 1 && type == VM_SECOND; // only allow metric prefix(es) if 'seconds' is used as unit type
2204 schoenebeck 3561 }
2205    
2206 schoenebeck 3188 VMFnResult* InstrumentScriptVMFunction_fade_out::exec(VMFnArgs* args) {
2207 schoenebeck 3587 StdUnit_t unit = args->arg(1)->asNumber()->unitType();
2208     vmint duration =
2209     (unit) ?
2210     args->arg(1)->asNumber()->evalCastInt(VM_MICRO) :
2211     args->arg(1)->asNumber()->evalCastInt();
2212 schoenebeck 3188 if (duration < 0) {
2213     wrnMsg("fade_out(): argument 2 may not be negative");
2214     duration = 0;
2215     }
2216     const float fDuration = float(duration) / 1000000.f; // convert microseconds to seconds
2217    
2218     bool stop = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : true;
2219    
2220     AbstractEngineChannel* pEngineChannel =
2221     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
2222    
2223     if (args->arg(0)->exprType() == INT_EXPR) {
2224     const ScriptID id = args->arg(0)->asInt()->evalInt();
2225     if (!id) {
2226     wrnMsg("fade_out(): note ID for argument 1 may not be zero");
2227     return successResult();
2228     }
2229     if (!id.isNoteID()) {
2230     wrnMsg("fade_out(): argument 1 is not a note ID");
2231     return successResult();
2232     }
2233    
2234     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
2235     if (!pNote) return successResult();
2236    
2237     // if fade_out() was called immediately after note was triggered
2238     // then immediately apply fade out duration to Note object
2239 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
2240 schoenebeck 3188 pNote->Override.VolumeTime = fDuration;
2241     } else { // otherwise schedule a "volume time" change with the requested fade out duration ...
2242     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
2243     e.Init(); // clear IDs
2244     e.Type = Event::type_note_synth_param;
2245     e.Param.NoteSynthParam.NoteID = id.noteID();
2246     e.Param.NoteSynthParam.Type = Event::synth_param_volume_time;
2247     e.Param.NoteSynthParam.Delta = fDuration;
2248 schoenebeck 3561 e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
2249 schoenebeck 3188
2250     pEngineChannel->ScheduleEventMicroSec(&e, 0);
2251     }
2252     // now schedule a "volume" change, simply one time slice ahead, with
2253     // the final fade out volume (0.0)
2254     {
2255     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
2256     e.Init(); // clear IDs
2257     e.Type = Event::type_note_synth_param;
2258     e.Param.NoteSynthParam.NoteID = id.noteID();
2259     e.Param.NoteSynthParam.Type = Event::synth_param_volume;
2260     e.Param.NoteSynthParam.Delta = 0.f;
2261 schoenebeck 3561 e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
2262 schoenebeck 3188
2263     // scheduling with 0 delay would also work here, but +1 is more
2264     // safe regarding potential future implementation changes of the
2265     // scheduler (see API comments of RTAVLTree::insert())
2266     pEngineChannel->ScheduleEventMicroSec(&e, 1);
2267     }
2268     // and finally if stopping the note was requested after the fade out
2269     // completed, then schedule to kill the voice after the requested
2270     // duration
2271     if (stop) {
2272     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
2273     e.Init(); // clear IDs
2274     e.Type = Event::type_kill_note;
2275     e.Param.Note.ID = id.noteID();
2276     e.Param.Note.Key = pNote->hostKey;
2277    
2278     pEngineChannel->ScheduleEventMicroSec(&e, duration + 1);
2279     }
2280     } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
2281     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
2282 schoenebeck 3557 for (vmint i = 0; i < ids->arraySize(); ++i) {
2283 schoenebeck 3188 const ScriptID id = ids->evalIntElement(i);
2284     if (!id || !id.isNoteID()) continue;
2285    
2286     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
2287     if (!pNote) continue;
2288    
2289     // if fade_out() was called immediately after note was triggered
2290     // then immediately apply fade out duration to Note object
2291 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
2292 schoenebeck 3188 pNote->Override.VolumeTime = fDuration;
2293     } else { // otherwise schedule a "volume time" change with the requested fade out duration ...
2294     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
2295     e.Init(); // clear IDs
2296     e.Type = Event::type_note_synth_param;
2297     e.Param.NoteSynthParam.NoteID = id.noteID();
2298     e.Param.NoteSynthParam.Type = Event::synth_param_volume_time;
2299     e.Param.NoteSynthParam.Delta = fDuration;
2300 schoenebeck 3561 e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
2301 schoenebeck 3188
2302     pEngineChannel->ScheduleEventMicroSec(&e, 0);
2303     }
2304     // now schedule a "volume" change, simply one time slice ahead, with
2305     // the final fade out volume (0.0)
2306     {
2307     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
2308     e.Init(); // clear IDs
2309     e.Type = Event::type_note_synth_param;
2310     e.Param.NoteSynthParam.NoteID = id.noteID();
2311     e.Param.NoteSynthParam.Type = Event::synth_param_volume;
2312     e.Param.NoteSynthParam.Delta = 0.f;
2313 schoenebeck 3561 e.Param.NoteSynthParam.Scope = Event::ValueScope::RELATIVE; // actually ignored
2314 schoenebeck 3188
2315     // scheduling with 0 delay would also work here, but +1 is more
2316     // safe regarding potential future implementation changes of the
2317     // scheduler (see API comments of RTAVLTree::insert())
2318     pEngineChannel->ScheduleEventMicroSec(&e, 1);
2319     }
2320     // and finally if stopping the note was requested after the fade out
2321     // completed, then schedule to kill the voice after the requested
2322     // duration
2323     if (stop) {
2324     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
2325     e.Init(); // clear IDs
2326     e.Type = Event::type_kill_note;
2327     e.Param.Note.ID = id.noteID();
2328     e.Param.Note.Key = pNote->hostKey;
2329 schoenebeck 3743
2330 schoenebeck 3188 pEngineChannel->ScheduleEventMicroSec(&e, duration + 1);
2331     }
2332     }
2333     }
2334    
2335     return successResult();
2336     }
2337    
2338 schoenebeck 3193 // get_event_par() function
2339    
2340     InstrumentScriptVMFunction_get_event_par::InstrumentScriptVMFunction_get_event_par(InstrumentScriptVM* parent)
2341     : m_vm(parent)
2342     {
2343     }
2344    
2345     VMFnResult* InstrumentScriptVMFunction_get_event_par::exec(VMFnArgs* args) {
2346     AbstractEngineChannel* pEngineChannel =
2347     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
2348    
2349     const ScriptID id = args->arg(0)->asInt()->evalInt();
2350     if (!id) {
2351     wrnMsg("get_event_par(): note ID for argument 1 may not be zero");
2352     return successResult(0);
2353     }
2354     if (!id.isNoteID()) {
2355     wrnMsg("get_event_par(): argument 1 is not a note ID");
2356     return successResult(0);
2357     }
2358    
2359     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
2360     if (!pNote) {
2361     wrnMsg("get_event_par(): no note alive with that note ID of argument 1");
2362     return successResult(0);
2363     }
2364    
2365 schoenebeck 3557 const vmint parameter = args->arg(1)->asInt()->evalInt();
2366 schoenebeck 3193 switch (parameter) {
2367     case EVENT_PAR_NOTE:
2368     return successResult(pNote->cause.Param.Note.Key);
2369     case EVENT_PAR_VELOCITY:
2370     return successResult(pNote->cause.Param.Note.Velocity);
2371     case EVENT_PAR_VOLUME:
2372     return successResult(
2373 schoenebeck 3561 RTMath::LinRatioToDecibel(pNote->Override.Volume.Value) * 1000.f
2374 schoenebeck 3193 );
2375     case EVENT_PAR_TUNE:
2376     return successResult(
2377 schoenebeck 3561 RTMath::FreqRatioToCents(pNote->Override.Pitch.Value) * 1000.f
2378 schoenebeck 3193 );
2379     case EVENT_PAR_0:
2380     return successResult(pNote->userPar[0]);
2381     case EVENT_PAR_1:
2382     return successResult(pNote->userPar[1]);
2383     case EVENT_PAR_2:
2384     return successResult(pNote->userPar[2]);
2385     case EVENT_PAR_3:
2386     return successResult(pNote->userPar[3]);
2387     }
2388    
2389     wrnMsg("get_event_par(): argument 2 is an invalid event parameter");
2390     return successResult(0);
2391     }
2392    
2393     // set_event_par() function
2394    
2395     InstrumentScriptVMFunction_set_event_par::InstrumentScriptVMFunction_set_event_par(InstrumentScriptVM* parent)
2396     : m_vm(parent)
2397     {
2398     }
2399    
2400     VMFnResult* InstrumentScriptVMFunction_set_event_par::exec(VMFnArgs* args) {
2401     AbstractEngineChannel* pEngineChannel =
2402     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
2403    
2404     const ScriptID id = args->arg(0)->asInt()->evalInt();
2405     if (!id) {
2406     wrnMsg("set_event_par(): note ID for argument 1 may not be zero");
2407     return successResult();
2408     }
2409     if (!id.isNoteID()) {
2410     wrnMsg("set_event_par(): argument 1 is not a note ID");
2411     return successResult();
2412     }
2413    
2414     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
2415     if (!pNote) return successResult();
2416    
2417 schoenebeck 3557 const vmint parameter = args->arg(1)->asInt()->evalInt();
2418     const vmint value = args->arg(2)->asInt()->evalInt();
2419 schoenebeck 3193
2420     switch (parameter) {
2421     case EVENT_PAR_NOTE:
2422     if (value < 0 || value > 127) {
2423     wrnMsg("set_event_par(): note number of argument 3 is out of range");
2424     return successResult();
2425     }
2426 schoenebeck 3214 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
2427 schoenebeck 3193 pNote->cause.Param.Note.Key = value;
2428 schoenebeck 3214 m_vm->m_event->cause.Param.Note.Key = value;
2429     } else {
2430 schoenebeck 3193 wrnMsg("set_event_par(): note number can only be changed when note is new");
2431 schoenebeck 3214 }
2432 schoenebeck 3193 return successResult();
2433     case EVENT_PAR_VELOCITY:
2434     if (value < 0 || value > 127) {
2435     wrnMsg("set_event_par(): velocity of argument 3 is out of range");
2436     return successResult();
2437     }
2438 schoenebeck 3214 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
2439 schoenebeck 3193 pNote->cause.Param.Note.Velocity = value;
2440 schoenebeck 3214 m_vm->m_event->cause.Param.Note.Velocity = value;
2441     } else {
2442 schoenebeck 3193 wrnMsg("set_event_par(): velocity can only be changed when note is new");
2443 schoenebeck 3214 }
2444 schoenebeck 3193 return successResult();
2445     case EVENT_PAR_VOLUME:
2446     wrnMsg("set_event_par(): changing volume by this function is currently not supported, use change_vol() instead");
2447     return successResult();
2448     case EVENT_PAR_TUNE:
2449     wrnMsg("set_event_par(): changing tune by this function is currently not supported, use change_tune() instead");
2450     return successResult();
2451     case EVENT_PAR_0:
2452     pNote->userPar[0] = value;
2453     return successResult();
2454     case EVENT_PAR_1:
2455     pNote->userPar[1] = value;
2456     return successResult();
2457     case EVENT_PAR_2:
2458     pNote->userPar[2] = value;
2459     return successResult();
2460     case EVENT_PAR_3:
2461     pNote->userPar[3] = value;
2462     return successResult();
2463     }
2464    
2465     wrnMsg("set_event_par(): argument 2 is an invalid event parameter");
2466     return successResult();
2467     }
2468    
2469 schoenebeck 3214 // change_note() function
2470    
2471     InstrumentScriptVMFunction_change_note::InstrumentScriptVMFunction_change_note(InstrumentScriptVM* parent)
2472     : m_vm(parent)
2473     {
2474     }
2475    
2476     VMFnResult* InstrumentScriptVMFunction_change_note::exec(VMFnArgs* args) {
2477     AbstractEngineChannel* pEngineChannel =
2478     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
2479    
2480     const ScriptID id = args->arg(0)->asInt()->evalInt();
2481     if (!id) {
2482     wrnMsg("change_note(): note ID for argument 1 may not be zero");
2483     return successResult();
2484     }
2485     if (!id.isNoteID()) {
2486     wrnMsg("change_note(): argument 1 is not a note ID");
2487     return successResult();
2488     }
2489    
2490     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
2491     if (!pNote) return successResult();
2492    
2493 schoenebeck 3557 const vmint value = args->arg(1)->asInt()->evalInt();
2494 schoenebeck 3214 if (value < 0 || value > 127) {
2495     wrnMsg("change_note(): note number of argument 2 is out of range");
2496     return successResult();
2497     }
2498    
2499     if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
2500     pNote->cause.Param.Note.Key = value;
2501     m_vm->m_event->cause.Param.Note.Key = value;
2502 schoenebeck 3216 } else {
2503 schoenebeck 3214 wrnMsg("change_note(): note number can only be changed when note is new");
2504     }
2505    
2506     return successResult();
2507     }
2508    
2509     // change_velo() function
2510    
2511     InstrumentScriptVMFunction_change_velo::InstrumentScriptVMFunction_change_velo(InstrumentScriptVM* parent)
2512     : m_vm(parent)
2513     {
2514     }
2515    
2516     VMFnResult* InstrumentScriptVMFunction_change_velo::exec(VMFnArgs* args) {
2517     AbstractEngineChannel* pEngineChannel =
2518     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
2519    
2520     const ScriptID id = args->arg(0)->asInt()->evalInt();
2521     if (!id) {
2522     wrnMsg("change_velo(): note ID for argument 1 may not be zero");
2523     return successResult();
2524     }
2525     if (!id.isNoteID()) {
2526     wrnMsg("change_velo(): argument 1 is not a note ID");
2527     return successResult();
2528     }
2529    
2530     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
2531     if (!pNote) return successResult();
2532    
2533 schoenebeck 3557 const vmint value = args->arg(1)->asInt()->evalInt();
2534 schoenebeck 3214 if (value < 0 || value > 127) {
2535     wrnMsg("change_velo(): velocity of argument 2 is out of range");
2536     return successResult();
2537     }
2538    
2539     if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
2540     pNote->cause.Param.Note.Velocity = value;
2541     m_vm->m_event->cause.Param.Note.Velocity = value;
2542 schoenebeck 3216 } else {
2543 schoenebeck 3214 wrnMsg("change_velo(): velocity can only be changed when note is new");
2544     }
2545    
2546     return successResult();
2547     }
2548    
2549 schoenebeck 3255 // change_play_pos() function
2550    
2551     InstrumentScriptVMFunction_change_play_pos::InstrumentScriptVMFunction_change_play_pos(InstrumentScriptVM* parent)
2552 schoenebeck 3587 : m_vm(parent)
2553 schoenebeck 3255 {
2554     }
2555    
2556 schoenebeck 3587 bool InstrumentScriptVMFunction_change_play_pos::acceptsArgType(vmint iArg, ExprType_t type) const {
2557     if (iArg == 0)
2558     return type == INT_EXPR;
2559     else
2560     return type == INT_EXPR || type == REAL_EXPR;
2561     }
2562    
2563 schoenebeck 3561 bool InstrumentScriptVMFunction_change_play_pos::acceptsArgUnitType(vmint iArg, StdUnit_t type) const {
2564     if (iArg == 1)
2565     return type == VM_NO_UNIT || type == VM_SECOND;
2566     else
2567     return type == VM_NO_UNIT;
2568     }
2569    
2570 schoenebeck 3564 bool InstrumentScriptVMFunction_change_play_pos::acceptsArgUnitPrefix(vmint iArg, StdUnit_t type) const {
2571 schoenebeck 3587 return iArg == 1 && type == VM_SECOND; // only allow metric prefix(es) if 'seconds' is used as unit type
2572 schoenebeck 3561 }
2573    
2574 schoenebeck 3255 VMFnResult* InstrumentScriptVMFunction_change_play_pos::exec(VMFnArgs* args) {
2575 schoenebeck 3564 const ScriptID id = args->arg(0)->asInt()->evalInt();
2576 schoenebeck 3255 if (!id) {
2577     wrnMsg("change_play_pos(): note ID for argument 1 may not be zero");
2578     return successResult();
2579     }
2580     if (!id.isNoteID()) {
2581     wrnMsg("change_play_pos(): argument 1 is not a note ID");
2582     return successResult();
2583     }
2584    
2585 schoenebeck 3587 StdUnit_t unit = args->arg(1)->asNumber()->unitType();
2586     const vmint pos =
2587     (unit) ?
2588     args->arg(1)->asNumber()->evalCastInt(VM_MICRO) :
2589     args->arg(1)->asNumber()->evalCastInt();
2590 schoenebeck 3255 if (pos < 0) {
2591     wrnMsg("change_play_pos(): playback position of argument 2 may not be negative");
2592     return successResult();
2593     }
2594    
2595     AbstractEngineChannel* pEngineChannel =
2596     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
2597    
2598     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
2599     if (!pNote) return successResult();
2600    
2601 schoenebeck 3557 pNote->Override.SampleOffset =
2602     (decltype(pNote->Override.SampleOffset)) pos;
2603 schoenebeck 3255
2604     return successResult();
2605     }
2606    
2607 schoenebeck 2935 // event_status() function
2608    
2609     InstrumentScriptVMFunction_event_status::InstrumentScriptVMFunction_event_status(InstrumentScriptVM* parent)
2610     : m_vm(parent)
2611     {
2612     }
2613    
2614     VMFnResult* InstrumentScriptVMFunction_event_status::exec(VMFnArgs* args) {
2615     AbstractEngineChannel* pEngineChannel =
2616     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
2617    
2618     const ScriptID id = args->arg(0)->asInt()->evalInt();
2619     if (!id) {
2620     wrnMsg("event_status(): note ID for argument 1 may not be zero");
2621     return successResult(EVENT_STATUS_INACTIVE);
2622     }
2623     if (!id.isNoteID()) {
2624     wrnMsg("event_status(): argument 1 is not a note ID");
2625     return successResult(EVENT_STATUS_INACTIVE);
2626     }
2627    
2628     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
2629     return successResult(pNote ? EVENT_STATUS_NOTE_QUEUE : EVENT_STATUS_INACTIVE);
2630     }
2631    
2632 schoenebeck 3296 // callback_status() function
2633    
2634     InstrumentScriptVMFunction_callback_status::InstrumentScriptVMFunction_callback_status(InstrumentScriptVM* parent)
2635     : m_vm(parent)
2636     {
2637     }
2638    
2639     VMFnResult* InstrumentScriptVMFunction_callback_status::exec(VMFnArgs* args) {
2640     const script_callback_id_t id = args->arg(0)->asInt()->evalInt();
2641     if (!id) {
2642     wrnMsg("callback_status(): callback ID for argument 1 may not be zero");
2643     return successResult();
2644     }
2645    
2646     AbstractEngineChannel* pEngineChannel =
2647     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
2648    
2649     RTList<ScriptEvent>::Iterator itCallback = pEngineChannel->ScriptCallbackByID(id);
2650     if (!itCallback)
2651     return successResult(CALLBACK_STATUS_TERMINATED);
2652    
2653     return successResult(
2654     (m_vm->m_event->execCtx == itCallback->execCtx) ?
2655     CALLBACK_STATUS_RUNNING : CALLBACK_STATUS_QUEUE
2656     );
2657     }
2658    
2659 schoenebeck 2948 // wait() function (overrides core wait() implementation)
2660    
2661     InstrumentScriptVMFunction_wait::InstrumentScriptVMFunction_wait(InstrumentScriptVM* parent)
2662     : CoreVMFunction_wait(parent)
2663 schoenebeck 3743 {
2664 schoenebeck 2948 }
2665    
2666     VMFnResult* InstrumentScriptVMFunction_wait::exec(VMFnArgs* args) {
2667     InstrumentScriptVM* m_vm = (InstrumentScriptVM*) vm;
2668    
2669     // this might be set by passing 1 with the 2nd argument of built-in stop_wait() function
2670     if (m_vm->m_event->ignoreAllWaitCalls) return successResult();
2671    
2672     return CoreVMFunction_wait::exec(args);
2673     }
2674    
2675     // stop_wait() function
2676    
2677     InstrumentScriptVMFunction_stop_wait::InstrumentScriptVMFunction_stop_wait(InstrumentScriptVM* parent)
2678     : m_vm(parent)
2679 schoenebeck 3743 {
2680 schoenebeck 2948 }
2681    
2682     VMFnResult* InstrumentScriptVMFunction_stop_wait::exec(VMFnArgs* args) {
2683     AbstractEngineChannel* pEngineChannel =
2684     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
2685    
2686     const script_callback_id_t id = args->arg(0)->asInt()->evalInt();
2687     if (!id) {
2688     wrnMsg("stop_wait(): callback ID for argument 1 may not be zero");
2689     return successResult();
2690     }
2691    
2692     RTList<ScriptEvent>::Iterator itCallback = pEngineChannel->ScriptCallbackByID(id);
2693     if (!itCallback) return successResult(); // ignore if callback is i.e. not alive anymore
2694    
2695     const bool disableWaitForever =
2696     (args->argsCount() >= 2) ? (args->arg(1)->asInt()->evalInt() == 1) : false;
2697    
2698     pEngineChannel->ScheduleResumeOfScriptCallback(
2699 schoenebeck 3205 itCallback, m_vm->m_event->scheduleTime, disableWaitForever
2700 schoenebeck 2948 );
2701    
2702     return successResult();
2703     }
2704    
2705 schoenebeck 3277 // abort() function
2706    
2707     InstrumentScriptVMFunction_abort::InstrumentScriptVMFunction_abort(InstrumentScriptVM* parent)
2708 schoenebeck 3293 : m_vm(parent)
2709 schoenebeck 3277 {
2710     }
2711    
2712     VMFnResult* InstrumentScriptVMFunction_abort::exec(VMFnArgs* args) {
2713     const script_callback_id_t id = args->arg(0)->asInt()->evalInt();
2714     if (!id) {
2715     wrnMsg("abort(): callback ID for argument 1 may not be zero");
2716     return successResult();
2717     }
2718    
2719     AbstractEngineChannel* pEngineChannel =
2720     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
2721    
2722     RTList<ScriptEvent>::Iterator itCallback = pEngineChannel->ScriptCallbackByID(id);
2723     if (!itCallback) return successResult(); // ignore if callback is i.e. not alive anymore
2724    
2725     itCallback->execCtx->signalAbort();
2726    
2727     return successResult();
2728     }
2729    
2730 schoenebeck 3293 // fork() function
2731    
2732     InstrumentScriptVMFunction_fork::InstrumentScriptVMFunction_fork(InstrumentScriptVM* parent)
2733     : m_vm(parent)
2734     {
2735     }
2736    
2737     VMFnResult* InstrumentScriptVMFunction_fork::exec(VMFnArgs* args) {
2738     // check if this is actually the parent going to fork, or rather one of
2739     // the children which is already forked
2740     if (m_vm->m_event->forkIndex != 0) { // this is the entry point for a child ...
2741     int forkResult = m_vm->m_event->forkIndex;
2742     // reset so that this child may i.e. also call fork() later on
2743     m_vm->m_event->forkIndex = 0;
2744     return successResult(forkResult);
2745     }
2746    
2747     // if we are here, then this is the parent, so we must fork this parent
2748    
2749 schoenebeck 3557 const vmint n =
2750 schoenebeck 3293 (args->argsCount() >= 1) ? args->arg(0)->asInt()->evalInt() : 1;
2751     const bool bAutoAbort =
2752     (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : true;
2753    
2754     if (m_vm->m_event->countChildHandlers() + n > MAX_FORK_PER_SCRIPT_HANDLER) {
2755     wrnMsg("fork(): requested amount would exceed allowed limit per event handler");
2756     return successResult(-1);
2757     }
2758    
2759     AbstractEngineChannel* pEngineChannel =
2760     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
2761    
2762     if (!pEngineChannel->hasFreeScriptCallbacks(n)) {
2763     wrnMsg("fork(): global limit of event handlers exceeded");
2764     return successResult(-1);
2765     }
2766    
2767     for (int iChild = 0; iChild < n; ++iChild) {
2768     RTList<ScriptEvent>::Iterator itChild =
2769     pEngineChannel->forkScriptCallback(m_vm->m_event, bAutoAbort);
2770     if (!itChild) { // should never happen, otherwise its a bug ...
2771     errMsg("fork(): internal error while allocating child");
2772     return errorResult(-1); // terminate script
2773     }
2774     // since both parent, as well all child script execution instances
2775 schoenebeck 3557 // all land in this exec() method, the following is (more or less)
2776 schoenebeck 3293 // the only feature that lets us distinguish the parent and
2777 schoenebeck 3557 // respective children from each other in this exec() method
2778 schoenebeck 3293 itChild->forkIndex = iChild + 1;
2779     }
2780    
2781     return successResult(0);
2782     }
2783    
2784 schoenebeck 2596 } // namespace LinuxSampler

  ViewVC Help
Powered by ViewVC