/[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 3561 - (hide annotations) (download)
Fri Aug 23 11:44:00 2019 UTC (4 years, 8 months ago) by schoenebeck
File size: 106557 byte(s)
NKSP: Added standard units support for numbers and final "!" operator:

* NKSP strictness: Variable names, function names and preprocessor condition
  names must start with a regular character (a-z or A-Z); starting them with
  a digit or underscore is no longer allowed.

* NKSP parser fix: equal comparison operator "=" and not equal comparison
  operator "#" must only accept integer operands.

* NKSP language: Implemented support for standard units like Hertz, seconds,
  Bel including support for metric unit prefixes; so one can now e.g.
  conveniently use numbers in scripts like "5us" meaning "5 microseconds",
  or e.g. "12kHz" meaning "12 kilo Hertz", or e.g. "-14mdB" meaning
  "minus 14 Millidecibel", or e.g. "28c" meaning "28 cents" (for tuning).

* NKSP language: Introduced "final" operator "!" which is specifically
  intended for synthesis parameter values to denote that the synthesis
  parameter value is intended to be the "final" value for that synthesis
  parameter that should explicitly be used by the engine and thus causing
  the sampler engine to ignore all other modulation sources for the same
  synthesis parameter (like e.g. LFO, EG); by simply prefixing a value,
  variable or formula with this new "!" operator the expression is marked as
  being "final".

* Bumped version (2.1.1.svn4).

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

  ViewVC Help
Powered by ViewVC