/[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 3360 - (hide annotations) (download)
Fri Oct 27 21:19:18 2017 UTC (3 weeks, 5 days ago) by schoenebeck
File size: 90062 byte(s)
* NKSP: Added built-in script function "change_cutoff_attack()".
* NKSP: Added built-in script function "change_cutoff_decay()".
* NKSP: Added built-in script function "change_cutoff_sustain()".
* NKSP: Added built-in script function "change_cutoff_release()".
* NKSP: Added built-in script function "change_cutoff_lfo_depth()".
* NKSP: Added built-in script function "change_cutoff_lfo_freq()".
* Bumped version (2.0.0.svn77).

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

  ViewVC Help
Powered by ViewVC