/[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 3335 - (hide annotations) (download)
Sun Jul 30 14:33:15 2017 UTC (7 weeks, 6 days ago) by schoenebeck
File size: 87756 byte(s)
* NKSP: Added built-in script function "change_pan_time()".
* NKSP: Added built-in script function "change_pan_curve()".
* Bumped version (2.0.0.svn75).

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     &NoteBase::_Override::Sustain,
1182     Event::synth_param_sustain,
1183 schoenebeck 3317 true, NO_LIMIT, 0>( args, "change_sustain" );
1184 schoenebeck 3316 }
1185    
1186 schoenebeck 3118 // change_amp_lfo_depth() function
1187    
1188     VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_depth::exec(VMFnArgs* args) {
1189 schoenebeck 3188 return VMChangeSynthParamFunction::execTemplate<
1190     &NoteBase::_Override::AmpLFODepth,
1191 schoenebeck 3193 Event::synth_param_amp_lfo_depth,
1192     true, 1000000, 0>( args, "change_amp_lfo_depth" );
1193 schoenebeck 3118 }
1194    
1195     // change_amp_lfo_freq() function
1196    
1197     VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_freq::exec(VMFnArgs* args) {
1198 schoenebeck 3188 return VMChangeSynthParamFunction::execTemplate<
1199     &NoteBase::_Override::AmpLFOFreq,
1200 schoenebeck 3193 Event::synth_param_amp_lfo_freq,
1201     true, 1000000, 0>( args, "change_amp_lfo_freq" );
1202 schoenebeck 3118 }
1203    
1204     // change_pitch_lfo_depth() function
1205    
1206     VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_depth::exec(VMFnArgs* args) {
1207 schoenebeck 3188 return VMChangeSynthParamFunction::execTemplate<
1208     &NoteBase::_Override::PitchLFODepth,
1209 schoenebeck 3193 Event::synth_param_pitch_lfo_depth,
1210     true, 1000000, 0>( args, "change_pitch_lfo_depth" );
1211 schoenebeck 3118 }
1212    
1213     // change_pitch_lfo_freq() function
1214    
1215     VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_freq::exec(VMFnArgs* args) {
1216 schoenebeck 3188 return VMChangeSynthParamFunction::execTemplate<
1217     &NoteBase::_Override::PitchLFOFreq,
1218 schoenebeck 3193 Event::synth_param_pitch_lfo_freq,
1219     true, 1000000, 0>( args, "change_pitch_lfo_freq" );
1220 schoenebeck 3118 }
1221    
1222 schoenebeck 3188 // change_vol_time() function
1223    
1224     VMFnResult* InstrumentScriptVMFunction_change_vol_time::exec(VMFnArgs* args) {
1225     return VMChangeSynthParamFunction::execTemplate<
1226     &NoteBase::_Override::VolumeTime,
1227 schoenebeck 3193 Event::synth_param_volume_time,
1228     false, NO_LIMIT, 0>( args, "change_vol_time" );
1229 schoenebeck 3188 }
1230    
1231     // change_tune_time() function
1232    
1233     VMFnResult* InstrumentScriptVMFunction_change_tune_time::exec(VMFnArgs* args) {
1234     return VMChangeSynthParamFunction::execTemplate<
1235     &NoteBase::_Override::PitchTime,
1236 schoenebeck 3193 Event::synth_param_pitch_time,
1237     false, NO_LIMIT, 0>( args, "change_tune_time" );
1238 schoenebeck 3188 }
1239    
1240 schoenebeck 3335 // change_pan_time() function
1241    
1242     VMFnResult* InstrumentScriptVMFunction_change_pan_time::exec(VMFnArgs* args) {
1243     return VMChangeSynthParamFunction::execTemplate<
1244     &NoteBase::_Override::PanTime,
1245     Event::synth_param_pan_time,
1246     false, NO_LIMIT, 0>( args, "change_pan_time" );
1247     }
1248    
1249 schoenebeck 3246 // template for change_*_curve() functions
1250    
1251     bool VMChangeFadeCurveFunction::acceptsArgType(int iArg, ExprType_t type) const {
1252     if (iArg == 0)
1253     return type == INT_EXPR || type == INT_ARR_EXPR;
1254     else
1255     return type == INT_EXPR;
1256     }
1257    
1258     template<fade_curve_t NoteBase::_Override::*T_noteParam, int T_synthParam>
1259     VMFnResult* VMChangeFadeCurveFunction::execTemplate(VMFnArgs* args, const char* functionName) {
1260     int value = args->arg(1)->asInt()->evalInt();
1261     switch (value) {
1262     case FADE_CURVE_LINEAR:
1263     case FADE_CURVE_EASE_IN_EASE_OUT:
1264     break;
1265     default:
1266     wrnMsg(String(functionName) + "(): invalid curve type passed as argument 2");
1267     return successResult();
1268     }
1269    
1270     AbstractEngineChannel* pEngineChannel =
1271     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1272    
1273     if (args->arg(0)->exprType() == INT_EXPR) {
1274     const ScriptID id = args->arg(0)->asInt()->evalInt();
1275     if (!id) {
1276     wrnMsg(String(functionName) + "(): note ID for argument 1 may not be zero");
1277     return successResult();
1278     }
1279     if (!id.isNoteID()) {
1280     wrnMsg(String(functionName) + "(): argument 1 is not a note ID");
1281     return successResult();
1282     }
1283    
1284     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1285     if (!pNote) return successResult();
1286    
1287     // if this change_*_curve() script function was called immediately after
1288     // note was triggered then immediately apply the synth parameter
1289     // change to Note object
1290     if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1291     pNote->Override.*T_noteParam = (fade_curve_t) value;
1292     } else { // otherwise schedule this synth parameter change ...
1293     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1294     e.Init(); // clear IDs
1295     e.Type = Event::type_note_synth_param;
1296     e.Param.NoteSynthParam.NoteID = id.noteID();
1297     e.Param.NoteSynthParam.Type = (Event::synth_param_t) T_synthParam;
1298     e.Param.NoteSynthParam.Delta = value;
1299     e.Param.NoteSynthParam.Relative = false;
1300    
1301     pEngineChannel->ScheduleEventMicroSec(&e, 0);
1302     }
1303     } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1304     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1305     for (int i = 0; i < ids->arraySize(); ++i) {
1306     const ScriptID id = ids->evalIntElement(i);
1307     if (!id || !id.isNoteID()) continue;
1308    
1309     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1310     if (!pNote) continue;
1311    
1312     // if this change_*_curve() script function was called immediately after
1313     // note was triggered then immediately apply the synth parameter
1314     // change to Note object
1315     if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1316     pNote->Override.*T_noteParam = (fade_curve_t) value;
1317     } else { // otherwise schedule this synth parameter change ...
1318     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1319     e.Init(); // clear IDs
1320     e.Type = Event::type_note_synth_param;
1321     e.Param.NoteSynthParam.NoteID = id.noteID();
1322     e.Param.NoteSynthParam.Type = (Event::synth_param_t) T_synthParam;
1323     e.Param.NoteSynthParam.Delta = value;
1324     e.Param.NoteSynthParam.Relative = false;
1325    
1326     pEngineChannel->ScheduleEventMicroSec(&e, 0);
1327     }
1328     }
1329     }
1330    
1331     return successResult();
1332     }
1333    
1334     // change_vol_curve() function
1335    
1336     VMFnResult* InstrumentScriptVMFunction_change_vol_curve::exec(VMFnArgs* args) {
1337     return VMChangeFadeCurveFunction::execTemplate<
1338     &NoteBase::_Override::VolumeCurve,
1339     Event::synth_param_volume_curve>( args, "change_vol_curve" );
1340     }
1341    
1342     // change_tune_curve() function
1343    
1344     VMFnResult* InstrumentScriptVMFunction_change_tune_curve::exec(VMFnArgs* args) {
1345     return VMChangeFadeCurveFunction::execTemplate<
1346     &NoteBase::_Override::PitchCurve,
1347     Event::synth_param_pitch_curve>( args, "change_tune_curve" );
1348     }
1349    
1350 schoenebeck 3335 // change_pan_curve() function
1351    
1352     VMFnResult* InstrumentScriptVMFunction_change_pan_curve::exec(VMFnArgs* args) {
1353     return VMChangeFadeCurveFunction::execTemplate<
1354     &NoteBase::_Override::PanCurve,
1355     Event::synth_param_pan_curve>( args, "change_pan_curve" );
1356     }
1357    
1358 schoenebeck 3188 // fade_in() function
1359    
1360     InstrumentScriptVMFunction_fade_in::InstrumentScriptVMFunction_fade_in(InstrumentScriptVM* parent)
1361     : m_vm(parent)
1362     {
1363     }
1364    
1365     bool InstrumentScriptVMFunction_fade_in::acceptsArgType(int iArg, ExprType_t type) const {
1366     if (iArg == 0)
1367     return type == INT_EXPR || type == INT_ARR_EXPR;
1368     else
1369     return type == INT_EXPR;
1370     }
1371    
1372     VMFnResult* InstrumentScriptVMFunction_fade_in::exec(VMFnArgs* args) {
1373     int duration = args->arg(1)->asInt()->evalInt();
1374     if (duration < 0) {
1375     wrnMsg("fade_in(): argument 2 may not be negative");
1376     duration = 0;
1377     }
1378     const float fDuration = float(duration) / 1000000.f; // convert microseconds to seconds
1379    
1380     AbstractEngineChannel* pEngineChannel =
1381     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1382    
1383     if (args->arg(0)->exprType() == INT_EXPR) {
1384     const ScriptID id = args->arg(0)->asInt()->evalInt();
1385     if (!id) {
1386     wrnMsg("fade_in(): note ID for argument 1 may not be zero");
1387     return successResult();
1388     }
1389     if (!id.isNoteID()) {
1390     wrnMsg("fade_in(): argument 1 is not a note ID");
1391     return successResult();
1392     }
1393    
1394     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1395     if (!pNote) return successResult();
1396    
1397     // if fade_in() was called immediately after note was triggered
1398     // then immediately apply a start volume of zero to Note object,
1399     // as well as the fade in duration
1400 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1401 schoenebeck 3188 pNote->Override.Volume = 0.f;
1402     pNote->Override.VolumeTime = fDuration;
1403     } else { // otherwise schedule a "volume time" change with the requested fade in duration ...
1404     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1405     e.Init(); // clear IDs
1406     e.Type = Event::type_note_synth_param;
1407     e.Param.NoteSynthParam.NoteID = id.noteID();
1408     e.Param.NoteSynthParam.Type = Event::synth_param_volume_time;
1409     e.Param.NoteSynthParam.Delta = fDuration;
1410     e.Param.NoteSynthParam.Relative = false;
1411    
1412     pEngineChannel->ScheduleEventMicroSec(&e, 0);
1413     }
1414     // and finally schedule a "volume" change, simply one time slice
1415     // ahead, with the final fade in volume (1.0)
1416     {
1417     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1418     e.Init(); // clear IDs
1419     e.Type = Event::type_note_synth_param;
1420     e.Param.NoteSynthParam.NoteID = id.noteID();
1421     e.Param.NoteSynthParam.Type = Event::synth_param_volume;
1422     e.Param.NoteSynthParam.Delta = 1.f;
1423     e.Param.NoteSynthParam.Relative = false;
1424    
1425     // scheduling with 0 delay would also work here, but +1 is more
1426     // safe regarding potential future implementation changes of the
1427     // scheduler (see API comments of RTAVLTree::insert())
1428     pEngineChannel->ScheduleEventMicroSec(&e, 1);
1429     }
1430     } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1431     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1432     for (int i = 0; i < ids->arraySize(); ++i) {
1433     const ScriptID id = ids->evalIntElement(i);
1434     if (!id || !id.isNoteID()) continue;
1435    
1436     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1437     if (!pNote) continue;
1438    
1439     // if fade_in() was called immediately after note was triggered
1440     // then immediately apply a start volume of zero to Note object,
1441     // as well as the fade in duration
1442 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1443 schoenebeck 3188 pNote->Override.Volume = 0.f;
1444     pNote->Override.VolumeTime = fDuration;
1445     } else { // otherwise schedule a "volume time" change with the requested fade in duration ...
1446     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1447     e.Init(); // clear IDs
1448     e.Type = Event::type_note_synth_param;
1449     e.Param.NoteSynthParam.NoteID = id.noteID();
1450     e.Param.NoteSynthParam.Type = Event::synth_param_volume_time;
1451     e.Param.NoteSynthParam.Delta = fDuration;
1452     e.Param.NoteSynthParam.Relative = false;
1453    
1454     pEngineChannel->ScheduleEventMicroSec(&e, 0);
1455     }
1456     // and finally schedule a "volume" change, simply one time slice
1457     // ahead, with the final fade in volume (1.0)
1458     {
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;
1464     e.Param.NoteSynthParam.Delta = 1.f;
1465     e.Param.NoteSynthParam.Relative = false;
1466    
1467     // scheduling with 0 delay would also work here, but +1 is more
1468     // safe regarding potential future implementation changes of the
1469     // scheduler (see API comments of RTAVLTree::insert())
1470     pEngineChannel->ScheduleEventMicroSec(&e, 1);
1471     }
1472     }
1473     }
1474    
1475     return successResult();
1476     }
1477    
1478     // fade_out() function
1479    
1480     InstrumentScriptVMFunction_fade_out::InstrumentScriptVMFunction_fade_out(InstrumentScriptVM* parent)
1481     : m_vm(parent)
1482     {
1483     }
1484    
1485     bool InstrumentScriptVMFunction_fade_out::acceptsArgType(int iArg, ExprType_t type) const {
1486     if (iArg == 0)
1487     return type == INT_EXPR || type == INT_ARR_EXPR;
1488     else
1489     return type == INT_EXPR;
1490     }
1491    
1492     VMFnResult* InstrumentScriptVMFunction_fade_out::exec(VMFnArgs* args) {
1493     int duration = args->arg(1)->asInt()->evalInt();
1494     if (duration < 0) {
1495     wrnMsg("fade_out(): argument 2 may not be negative");
1496     duration = 0;
1497     }
1498     const float fDuration = float(duration) / 1000000.f; // convert microseconds to seconds
1499    
1500     bool stop = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : true;
1501    
1502     AbstractEngineChannel* pEngineChannel =
1503     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1504    
1505     if (args->arg(0)->exprType() == INT_EXPR) {
1506     const ScriptID id = args->arg(0)->asInt()->evalInt();
1507     if (!id) {
1508     wrnMsg("fade_out(): note ID for argument 1 may not be zero");
1509     return successResult();
1510     }
1511     if (!id.isNoteID()) {
1512     wrnMsg("fade_out(): argument 1 is not a note ID");
1513     return successResult();
1514     }
1515    
1516     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1517     if (!pNote) return successResult();
1518    
1519     // if fade_out() was called immediately after note was triggered
1520     // then immediately apply fade out duration to Note object
1521 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1522 schoenebeck 3188 pNote->Override.VolumeTime = fDuration;
1523     } else { // otherwise schedule a "volume time" change with the requested fade out duration ...
1524     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1525     e.Init(); // clear IDs
1526     e.Type = Event::type_note_synth_param;
1527     e.Param.NoteSynthParam.NoteID = id.noteID();
1528     e.Param.NoteSynthParam.Type = Event::synth_param_volume_time;
1529     e.Param.NoteSynthParam.Delta = fDuration;
1530     e.Param.NoteSynthParam.Relative = false;
1531    
1532     pEngineChannel->ScheduleEventMicroSec(&e, 0);
1533     }
1534     // now schedule a "volume" change, simply one time slice ahead, with
1535     // the final fade out volume (0.0)
1536     {
1537     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1538     e.Init(); // clear IDs
1539     e.Type = Event::type_note_synth_param;
1540     e.Param.NoteSynthParam.NoteID = id.noteID();
1541     e.Param.NoteSynthParam.Type = Event::synth_param_volume;
1542     e.Param.NoteSynthParam.Delta = 0.f;
1543     e.Param.NoteSynthParam.Relative = false;
1544    
1545     // scheduling with 0 delay would also work here, but +1 is more
1546     // safe regarding potential future implementation changes of the
1547     // scheduler (see API comments of RTAVLTree::insert())
1548     pEngineChannel->ScheduleEventMicroSec(&e, 1);
1549     }
1550     // and finally if stopping the note was requested after the fade out
1551     // completed, then schedule to kill the voice after the requested
1552     // duration
1553     if (stop) {
1554     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1555     e.Init(); // clear IDs
1556     e.Type = Event::type_kill_note;
1557     e.Param.Note.ID = id.noteID();
1558     e.Param.Note.Key = pNote->hostKey;
1559    
1560     pEngineChannel->ScheduleEventMicroSec(&e, duration + 1);
1561     }
1562     } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1563     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1564     for (int i = 0; i < ids->arraySize(); ++i) {
1565     const ScriptID id = ids->evalIntElement(i);
1566     if (!id || !id.isNoteID()) continue;
1567    
1568     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1569     if (!pNote) continue;
1570    
1571     // if fade_out() was called immediately after note was triggered
1572     // then immediately apply fade out duration to Note object
1573 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1574 schoenebeck 3188 pNote->Override.VolumeTime = fDuration;
1575     } else { // otherwise schedule a "volume time" change with the requested fade out duration ...
1576     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1577     e.Init(); // clear IDs
1578     e.Type = Event::type_note_synth_param;
1579     e.Param.NoteSynthParam.NoteID = id.noteID();
1580     e.Param.NoteSynthParam.Type = Event::synth_param_volume_time;
1581     e.Param.NoteSynthParam.Delta = fDuration;
1582     e.Param.NoteSynthParam.Relative = false;
1583    
1584     pEngineChannel->ScheduleEventMicroSec(&e, 0);
1585     }
1586     // now schedule a "volume" change, simply one time slice ahead, with
1587     // the final fade out volume (0.0)
1588     {
1589     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1590     e.Init(); // clear IDs
1591     e.Type = Event::type_note_synth_param;
1592     e.Param.NoteSynthParam.NoteID = id.noteID();
1593     e.Param.NoteSynthParam.Type = Event::synth_param_volume;
1594     e.Param.NoteSynthParam.Delta = 0.f;
1595     e.Param.NoteSynthParam.Relative = false;
1596    
1597     // scheduling with 0 delay would also work here, but +1 is more
1598     // safe regarding potential future implementation changes of the
1599     // scheduler (see API comments of RTAVLTree::insert())
1600     pEngineChannel->ScheduleEventMicroSec(&e, 1);
1601     }
1602     // and finally if stopping the note was requested after the fade out
1603     // completed, then schedule to kill the voice after the requested
1604     // duration
1605     if (stop) {
1606     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1607     e.Init(); // clear IDs
1608     e.Type = Event::type_kill_note;
1609     e.Param.Note.ID = id.noteID();
1610     e.Param.Note.Key = pNote->hostKey;
1611    
1612     pEngineChannel->ScheduleEventMicroSec(&e, duration + 1);
1613     }
1614     }
1615     }
1616    
1617     return successResult();
1618     }
1619    
1620 schoenebeck 3193 // get_event_par() function
1621    
1622     InstrumentScriptVMFunction_get_event_par::InstrumentScriptVMFunction_get_event_par(InstrumentScriptVM* parent)
1623     : m_vm(parent)
1624     {
1625     }
1626    
1627     VMFnResult* InstrumentScriptVMFunction_get_event_par::exec(VMFnArgs* args) {
1628     AbstractEngineChannel* pEngineChannel =
1629     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1630    
1631     const ScriptID id = args->arg(0)->asInt()->evalInt();
1632     if (!id) {
1633     wrnMsg("get_event_par(): note ID for argument 1 may not be zero");
1634     return successResult(0);
1635     }
1636     if (!id.isNoteID()) {
1637     wrnMsg("get_event_par(): argument 1 is not a note ID");
1638     return successResult(0);
1639     }
1640    
1641     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1642     if (!pNote) {
1643     wrnMsg("get_event_par(): no note alive with that note ID of argument 1");
1644     return successResult(0);
1645     }
1646    
1647     const int parameter = args->arg(1)->asInt()->evalInt();
1648     switch (parameter) {
1649     case EVENT_PAR_NOTE:
1650     return successResult(pNote->cause.Param.Note.Key);
1651     case EVENT_PAR_VELOCITY:
1652     return successResult(pNote->cause.Param.Note.Velocity);
1653     case EVENT_PAR_VOLUME:
1654     return successResult(
1655     RTMath::LinRatioToDecibel(pNote->Override.Volume) * 1000.f
1656     );
1657     case EVENT_PAR_TUNE:
1658     return successResult(
1659     RTMath::FreqRatioToCents(pNote->Override.Pitch) * 1000.f
1660     );
1661     case EVENT_PAR_0:
1662     return successResult(pNote->userPar[0]);
1663     case EVENT_PAR_1:
1664     return successResult(pNote->userPar[1]);
1665     case EVENT_PAR_2:
1666     return successResult(pNote->userPar[2]);
1667     case EVENT_PAR_3:
1668     return successResult(pNote->userPar[3]);
1669     }
1670    
1671     wrnMsg("get_event_par(): argument 2 is an invalid event parameter");
1672     return successResult(0);
1673     }
1674    
1675     // set_event_par() function
1676    
1677     InstrumentScriptVMFunction_set_event_par::InstrumentScriptVMFunction_set_event_par(InstrumentScriptVM* parent)
1678     : m_vm(parent)
1679     {
1680     }
1681    
1682     VMFnResult* InstrumentScriptVMFunction_set_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("set_event_par(): note ID for argument 1 may not be zero");
1689     return successResult();
1690     }
1691     if (!id.isNoteID()) {
1692     wrnMsg("set_event_par(): argument 1 is not a note ID");
1693     return successResult();
1694     }
1695    
1696     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1697     if (!pNote) return successResult();
1698    
1699     const int parameter = args->arg(1)->asInt()->evalInt();
1700     const int value = args->arg(2)->asInt()->evalInt();
1701    
1702     switch (parameter) {
1703     case EVENT_PAR_NOTE:
1704     if (value < 0 || value > 127) {
1705     wrnMsg("set_event_par(): note number of argument 3 is out of range");
1706     return successResult();
1707     }
1708 schoenebeck 3214 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1709 schoenebeck 3193 pNote->cause.Param.Note.Key = value;
1710 schoenebeck 3214 m_vm->m_event->cause.Param.Note.Key = value;
1711     } else {
1712 schoenebeck 3193 wrnMsg("set_event_par(): note number can only be changed when note is new");
1713 schoenebeck 3214 }
1714 schoenebeck 3193 return successResult();
1715     case EVENT_PAR_VELOCITY:
1716     if (value < 0 || value > 127) {
1717     wrnMsg("set_event_par(): velocity of argument 3 is out of range");
1718     return successResult();
1719     }
1720 schoenebeck 3214 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1721 schoenebeck 3193 pNote->cause.Param.Note.Velocity = value;
1722 schoenebeck 3214 m_vm->m_event->cause.Param.Note.Velocity = value;
1723     } else {
1724 schoenebeck 3193 wrnMsg("set_event_par(): velocity can only be changed when note is new");
1725 schoenebeck 3214 }
1726 schoenebeck 3193 return successResult();
1727     case EVENT_PAR_VOLUME:
1728     wrnMsg("set_event_par(): changing volume by this function is currently not supported, use change_vol() instead");
1729     return successResult();
1730     case EVENT_PAR_TUNE:
1731     wrnMsg("set_event_par(): changing tune by this function is currently not supported, use change_tune() instead");
1732     return successResult();
1733     case EVENT_PAR_0:
1734     pNote->userPar[0] = value;
1735     return successResult();
1736     case EVENT_PAR_1:
1737     pNote->userPar[1] = value;
1738     return successResult();
1739     case EVENT_PAR_2:
1740     pNote->userPar[2] = value;
1741     return successResult();
1742     case EVENT_PAR_3:
1743     pNote->userPar[3] = value;
1744     return successResult();
1745     }
1746    
1747     wrnMsg("set_event_par(): argument 2 is an invalid event parameter");
1748     return successResult();
1749     }
1750    
1751 schoenebeck 3214 // change_note() function
1752    
1753     InstrumentScriptVMFunction_change_note::InstrumentScriptVMFunction_change_note(InstrumentScriptVM* parent)
1754     : m_vm(parent)
1755     {
1756     }
1757    
1758     VMFnResult* InstrumentScriptVMFunction_change_note::exec(VMFnArgs* args) {
1759     AbstractEngineChannel* pEngineChannel =
1760     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1761    
1762     const ScriptID id = args->arg(0)->asInt()->evalInt();
1763     if (!id) {
1764     wrnMsg("change_note(): note ID for argument 1 may not be zero");
1765     return successResult();
1766     }
1767     if (!id.isNoteID()) {
1768     wrnMsg("change_note(): argument 1 is not a note ID");
1769     return successResult();
1770     }
1771    
1772     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1773     if (!pNote) return successResult();
1774    
1775     const int value = args->arg(1)->asInt()->evalInt();
1776     if (value < 0 || value > 127) {
1777     wrnMsg("change_note(): note number of argument 2 is out of range");
1778     return successResult();
1779     }
1780    
1781     if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1782     pNote->cause.Param.Note.Key = value;
1783     m_vm->m_event->cause.Param.Note.Key = value;
1784 schoenebeck 3216 } else {
1785 schoenebeck 3214 wrnMsg("change_note(): note number can only be changed when note is new");
1786     }
1787    
1788     return successResult();
1789     }
1790    
1791     // change_velo() function
1792    
1793     InstrumentScriptVMFunction_change_velo::InstrumentScriptVMFunction_change_velo(InstrumentScriptVM* parent)
1794     : m_vm(parent)
1795     {
1796     }
1797    
1798     VMFnResult* InstrumentScriptVMFunction_change_velo::exec(VMFnArgs* args) {
1799     AbstractEngineChannel* pEngineChannel =
1800     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1801    
1802     const ScriptID id = args->arg(0)->asInt()->evalInt();
1803     if (!id) {
1804     wrnMsg("change_velo(): note ID for argument 1 may not be zero");
1805     return successResult();
1806     }
1807     if (!id.isNoteID()) {
1808     wrnMsg("change_velo(): argument 1 is not a note ID");
1809     return successResult();
1810     }
1811    
1812     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1813     if (!pNote) return successResult();
1814    
1815     const int value = args->arg(1)->asInt()->evalInt();
1816     if (value < 0 || value > 127) {
1817     wrnMsg("change_velo(): velocity of argument 2 is out of range");
1818     return successResult();
1819     }
1820    
1821     if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1822     pNote->cause.Param.Note.Velocity = value;
1823     m_vm->m_event->cause.Param.Note.Velocity = value;
1824 schoenebeck 3216 } else {
1825 schoenebeck 3214 wrnMsg("change_velo(): velocity can only be changed when note is new");
1826     }
1827    
1828     return successResult();
1829     }
1830    
1831 schoenebeck 3255 // change_play_pos() function
1832    
1833     InstrumentScriptVMFunction_change_play_pos::InstrumentScriptVMFunction_change_play_pos(InstrumentScriptVM* parent)
1834     : m_vm(parent)
1835     {
1836     }
1837    
1838     VMFnResult* InstrumentScriptVMFunction_change_play_pos::exec(VMFnArgs* args) {
1839     const ScriptID id = args->arg(0)->asInt()->evalInt();
1840     if (!id) {
1841     wrnMsg("change_play_pos(): note ID for argument 1 may not be zero");
1842     return successResult();
1843     }
1844     if (!id.isNoteID()) {
1845     wrnMsg("change_play_pos(): argument 1 is not a note ID");
1846     return successResult();
1847     }
1848    
1849     const int pos = args->arg(1)->asInt()->evalInt();
1850     if (pos < 0) {
1851     wrnMsg("change_play_pos(): playback position of argument 2 may not be negative");
1852     return successResult();
1853     }
1854    
1855     AbstractEngineChannel* pEngineChannel =
1856     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1857    
1858     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1859     if (!pNote) return successResult();
1860    
1861     pNote->Override.SampleOffset = pos;
1862    
1863     return successResult();
1864     }
1865    
1866 schoenebeck 2935 // event_status() function
1867    
1868     InstrumentScriptVMFunction_event_status::InstrumentScriptVMFunction_event_status(InstrumentScriptVM* parent)
1869     : m_vm(parent)
1870     {
1871     }
1872    
1873     VMFnResult* InstrumentScriptVMFunction_event_status::exec(VMFnArgs* args) {
1874     AbstractEngineChannel* pEngineChannel =
1875     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1876    
1877     const ScriptID id = args->arg(0)->asInt()->evalInt();
1878     if (!id) {
1879     wrnMsg("event_status(): note ID for argument 1 may not be zero");
1880     return successResult(EVENT_STATUS_INACTIVE);
1881     }
1882     if (!id.isNoteID()) {
1883     wrnMsg("event_status(): argument 1 is not a note ID");
1884     return successResult(EVENT_STATUS_INACTIVE);
1885     }
1886    
1887     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1888     return successResult(pNote ? EVENT_STATUS_NOTE_QUEUE : EVENT_STATUS_INACTIVE);
1889     }
1890    
1891 schoenebeck 3296 // callback_status() function
1892    
1893     InstrumentScriptVMFunction_callback_status::InstrumentScriptVMFunction_callback_status(InstrumentScriptVM* parent)
1894     : m_vm(parent)
1895     {
1896     }
1897    
1898     VMFnResult* InstrumentScriptVMFunction_callback_status::exec(VMFnArgs* args) {
1899     const script_callback_id_t id = args->arg(0)->asInt()->evalInt();
1900     if (!id) {
1901     wrnMsg("callback_status(): callback ID for argument 1 may not be zero");
1902     return successResult();
1903     }
1904    
1905     AbstractEngineChannel* pEngineChannel =
1906     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1907    
1908     RTList<ScriptEvent>::Iterator itCallback = pEngineChannel->ScriptCallbackByID(id);
1909     if (!itCallback)
1910     return successResult(CALLBACK_STATUS_TERMINATED);
1911    
1912     return successResult(
1913     (m_vm->m_event->execCtx == itCallback->execCtx) ?
1914     CALLBACK_STATUS_RUNNING : CALLBACK_STATUS_QUEUE
1915     );
1916     }
1917    
1918 schoenebeck 2948 // wait() function (overrides core wait() implementation)
1919    
1920     InstrumentScriptVMFunction_wait::InstrumentScriptVMFunction_wait(InstrumentScriptVM* parent)
1921     : CoreVMFunction_wait(parent)
1922     {
1923     }
1924    
1925     VMFnResult* InstrumentScriptVMFunction_wait::exec(VMFnArgs* args) {
1926     InstrumentScriptVM* m_vm = (InstrumentScriptVM*) vm;
1927    
1928     // this might be set by passing 1 with the 2nd argument of built-in stop_wait() function
1929     if (m_vm->m_event->ignoreAllWaitCalls) return successResult();
1930    
1931     return CoreVMFunction_wait::exec(args);
1932     }
1933    
1934     // stop_wait() function
1935    
1936     InstrumentScriptVMFunction_stop_wait::InstrumentScriptVMFunction_stop_wait(InstrumentScriptVM* parent)
1937     : m_vm(parent)
1938     {
1939     }
1940    
1941     VMFnResult* InstrumentScriptVMFunction_stop_wait::exec(VMFnArgs* args) {
1942     AbstractEngineChannel* pEngineChannel =
1943     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1944    
1945     const script_callback_id_t id = args->arg(0)->asInt()->evalInt();
1946     if (!id) {
1947     wrnMsg("stop_wait(): callback ID for argument 1 may not be zero");
1948     return successResult();
1949     }
1950    
1951     RTList<ScriptEvent>::Iterator itCallback = pEngineChannel->ScriptCallbackByID(id);
1952     if (!itCallback) return successResult(); // ignore if callback is i.e. not alive anymore
1953    
1954     const bool disableWaitForever =
1955     (args->argsCount() >= 2) ? (args->arg(1)->asInt()->evalInt() == 1) : false;
1956    
1957     pEngineChannel->ScheduleResumeOfScriptCallback(
1958 schoenebeck 3205 itCallback, m_vm->m_event->scheduleTime, disableWaitForever
1959 schoenebeck 2948 );
1960    
1961     return successResult();
1962     }
1963    
1964 schoenebeck 3277 // abort() function
1965    
1966     InstrumentScriptVMFunction_abort::InstrumentScriptVMFunction_abort(InstrumentScriptVM* parent)
1967 schoenebeck 3293 : m_vm(parent)
1968 schoenebeck 3277 {
1969     }
1970    
1971     VMFnResult* InstrumentScriptVMFunction_abort::exec(VMFnArgs* args) {
1972     const script_callback_id_t id = args->arg(0)->asInt()->evalInt();
1973     if (!id) {
1974     wrnMsg("abort(): callback ID for argument 1 may not be zero");
1975     return successResult();
1976     }
1977    
1978     AbstractEngineChannel* pEngineChannel =
1979     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1980    
1981     RTList<ScriptEvent>::Iterator itCallback = pEngineChannel->ScriptCallbackByID(id);
1982     if (!itCallback) return successResult(); // ignore if callback is i.e. not alive anymore
1983    
1984     itCallback->execCtx->signalAbort();
1985    
1986     return successResult();
1987     }
1988    
1989 schoenebeck 3293 // fork() function
1990    
1991     InstrumentScriptVMFunction_fork::InstrumentScriptVMFunction_fork(InstrumentScriptVM* parent)
1992     : m_vm(parent)
1993     {
1994     }
1995    
1996     VMFnResult* InstrumentScriptVMFunction_fork::exec(VMFnArgs* args) {
1997     // check if this is actually the parent going to fork, or rather one of
1998     // the children which is already forked
1999     if (m_vm->m_event->forkIndex != 0) { // this is the entry point for a child ...
2000     int forkResult = m_vm->m_event->forkIndex;
2001     // reset so that this child may i.e. also call fork() later on
2002     m_vm->m_event->forkIndex = 0;
2003     return successResult(forkResult);
2004     }
2005    
2006     // if we are here, then this is the parent, so we must fork this parent
2007    
2008     const int n =
2009     (args->argsCount() >= 1) ? args->arg(0)->asInt()->evalInt() : 1;
2010     const bool bAutoAbort =
2011     (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : true;
2012    
2013     if (m_vm->m_event->countChildHandlers() + n > MAX_FORK_PER_SCRIPT_HANDLER) {
2014     wrnMsg("fork(): requested amount would exceed allowed limit per event handler");
2015     return successResult(-1);
2016     }
2017    
2018     AbstractEngineChannel* pEngineChannel =
2019     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
2020    
2021     if (!pEngineChannel->hasFreeScriptCallbacks(n)) {
2022     wrnMsg("fork(): global limit of event handlers exceeded");
2023     return successResult(-1);
2024     }
2025    
2026     for (int iChild = 0; iChild < n; ++iChild) {
2027     RTList<ScriptEvent>::Iterator itChild =
2028     pEngineChannel->forkScriptCallback(m_vm->m_event, bAutoAbort);
2029     if (!itChild) { // should never happen, otherwise its a bug ...
2030     errMsg("fork(): internal error while allocating child");
2031     return errorResult(-1); // terminate script
2032     }
2033     // since both parent, as well all child script execution instances
2034     // all land in this exect() method, the following is (more or less)
2035     // the only feature that lets us distinguish the parent and
2036     // respective children from each other in this exect() method
2037     itChild->forkIndex = iChild + 1;
2038     }
2039    
2040     return successResult(0);
2041     }
2042    
2043 schoenebeck 2596 } // namespace LinuxSampler

  ViewVC Help
Powered by ViewVC