/[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 3587 - (hide annotations) (download)
Sat Aug 31 12:08:49 2019 UTC (4 years, 7 months ago) by schoenebeck
File size: 110614 byte(s)
NKSP: Real number support for core instrument built-in functions.

* NKSP: Built-in function "play_note()" accepts now real numbers and
  seconds as unit type as well for its 3rd and 4th function arguments.

* NKSP: The following built-in functions accept now real numbers as well for
  their 2nd function argument: "change_vol()", "change_tune()",
  "change_cutoff()", "change_attack()", "change_decay()",
  "change_release()", "change_sustain()", "change_cutoff_attack()",
  "change_cutoff_decay()", "change_cutoff_sustain()",
  "change_cutoff_release()", "change_amp_lfo_freq()",
  "change_cutoff_lfo_freq()", "change_pitch_lfo_freq()",
  "change_vol_time()", "change_tune_time()", "change_pan_time()",
  "fade_in()", "fade_out()", "change_play_pos()".

* NKSP: Fixed built-in function "change_play_pos()" not having accepted
  metric prefixes at all.

* Bumped version (2.1.1.svn12).

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

  ViewVC Help
Powered by ViewVC