/[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 3557 - (hide annotations) (download)
Sun Aug 18 00:06:04 2019 UTC (4 years, 8 months ago) by schoenebeck
File size: 90309 byte(s)
* NKSP: Introducing 64 bit support for NKSP integer scripts
  variables (declare $foo).
* Require C++11 compiler support.
* Autoconf: Added m4/ax_cxx_compile_stdcxx.m4 macro which is used
  for checking in configure for C++11 support (as mandatory
  requirement) and automatically adds compiler argument if required
  (e.g. -std=C++11).
* Bumped version (2.1.1.svn3).

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

  ViewVC Help
Powered by ViewVC