/[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 3293 - (hide annotations) (download)
Tue Jun 27 22:19:19 2017 UTC (6 years, 9 months ago) by schoenebeck
File size: 85953 byte(s)
* NKSP: Added built-in script function "fork()".
* NKSP: Added built-in array variable %NKSP_CALLBACK_CHILD_ID[].
* NKSP: Added built-in variable $NKSP_CALLBACK_PARENT_ID.
* NKSP: Fixed potential crash when accessing dynamic built-in
  array variables.
* Bumped version (2.0.0.svn65).

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     if (attack > VM_EG_PAR_MAX_VALUE) {
842     wrnMsg("change_attack(): argument 2 may not be larger than 1000000");
843     attack = VM_EG_PAR_MAX_VALUE;
844     } else if (attack < 0) {
845     wrnMsg("change_attack(): argument 2 may not be negative");
846     attack = 0;
847     }
848     const float fAttack = float(attack) / float(VM_EG_PAR_MAX_VALUE);
849    
850     AbstractEngineChannel* pEngineChannel =
851     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
852    
853     if (args->arg(0)->exprType() == INT_EXPR) {
854     const ScriptID id = args->arg(0)->asInt()->evalInt();
855     if (!id) {
856     wrnMsg("change_attack(): note ID for argument 1 may not be zero");
857     return successResult();
858     }
859     if (!id.isNoteID()) {
860     wrnMsg("change_attack(): argument 1 is not a note ID");
861     return successResult();
862     }
863    
864     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
865 schoenebeck 2962 if (!pNote) return successResult();
866 schoenebeck 2953
867 schoenebeck 2962 // if change_attack() was called immediately after note was triggered
868     // then immediately apply attack to Note object
869 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
870 schoenebeck 2962 pNote->Override.Attack = fAttack;
871     } else { // otherwise schedule attack change ...
872     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
873     e.Init(); // clear IDs
874     e.Type = Event::type_note_synth_param;
875     e.Param.NoteSynthParam.NoteID = id.noteID();
876     e.Param.NoteSynthParam.Type = Event::synth_param_attack;
877     e.Param.NoteSynthParam.Delta = fAttack;
878     e.Param.NoteSynthParam.Relative = false;
879 schoenebeck 2953
880 schoenebeck 2962 pEngineChannel->ScheduleEventMicroSec(&e, 0);
881     }
882 schoenebeck 2953 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
883     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
884     for (int i = 0; i < ids->arraySize(); ++i) {
885     const ScriptID id = ids->evalIntElement(i);
886     if (!id || !id.isNoteID()) continue;
887    
888     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
889     if (!pNote) continue;
890    
891 schoenebeck 2962 // if change_attack() was called immediately after note was triggered
892     // then immediately apply attack to Note object
893 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
894 schoenebeck 2962 pNote->Override.Attack = fAttack;
895     } else { // otherwise schedule attack change ...
896     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
897     e.Init(); // clear IDs
898     e.Type = Event::type_note_synth_param;
899     e.Param.NoteSynthParam.NoteID = id.noteID();
900     e.Param.NoteSynthParam.Type = Event::synth_param_attack;
901     e.Param.NoteSynthParam.Delta = fAttack;
902     e.Param.NoteSynthParam.Relative = false;
903 schoenebeck 2953
904 schoenebeck 2962 pEngineChannel->ScheduleEventMicroSec(&e, 0);
905     }
906 schoenebeck 2953 }
907     }
908    
909     return successResult();
910     }
911    
912     // change_decay() function
913    
914     InstrumentScriptVMFunction_change_decay::InstrumentScriptVMFunction_change_decay(InstrumentScriptVM* parent)
915     : m_vm(parent)
916     {
917     }
918    
919     bool InstrumentScriptVMFunction_change_decay::acceptsArgType(int iArg, ExprType_t type) const {
920     if (iArg == 0)
921     return type == INT_EXPR || type == INT_ARR_EXPR;
922     else
923 schoenebeck 3188 return type == INT_EXPR;
924 schoenebeck 2953 }
925    
926     VMFnResult* InstrumentScriptVMFunction_change_decay::exec(VMFnArgs* args) {
927     int decay = args->arg(1)->asInt()->evalInt();
928     if (decay > VM_EG_PAR_MAX_VALUE) {
929     wrnMsg("change_decay(): argument 2 may not be larger than 1000000");
930     decay = VM_EG_PAR_MAX_VALUE;
931     } else if (decay < 0) {
932     wrnMsg("change_decay(): argument 2 may not be negative");
933     decay = 0;
934     }
935     const float fDecay = float(decay) / float(VM_EG_PAR_MAX_VALUE);
936    
937     AbstractEngineChannel* pEngineChannel =
938     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
939    
940     if (args->arg(0)->exprType() == INT_EXPR) {
941     const ScriptID id = args->arg(0)->asInt()->evalInt();
942     if (!id) {
943     wrnMsg("change_decay(): note ID for argument 1 may not be zero");
944     return successResult();
945     }
946     if (!id.isNoteID()) {
947     wrnMsg("change_decay(): argument 1 is not a note ID");
948     return successResult();
949     }
950    
951     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
952 schoenebeck 2962 if (!pNote) return successResult();
953 schoenebeck 2953
954 schoenebeck 2962 // if change_decay() was called immediately after note was triggered
955     // then immediately apply decay to Note object
956 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
957 schoenebeck 2962 pNote->Override.Decay = fDecay;
958     } else { // otherwise schedule decay change ...
959     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
960     e.Init(); // clear IDs
961     e.Type = Event::type_note_synth_param;
962     e.Param.NoteSynthParam.NoteID = id.noteID();
963     e.Param.NoteSynthParam.Type = Event::synth_param_decay;
964     e.Param.NoteSynthParam.Delta = fDecay;
965     e.Param.NoteSynthParam.Relative = false;
966 schoenebeck 2953
967 schoenebeck 2962 pEngineChannel->ScheduleEventMicroSec(&e, 0);
968     }
969 schoenebeck 2953 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
970     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
971     for (int i = 0; i < ids->arraySize(); ++i) {
972     const ScriptID id = ids->evalIntElement(i);
973     if (!id || !id.isNoteID()) continue;
974    
975     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
976     if (!pNote) continue;
977    
978 schoenebeck 2962 // if change_decay() was called immediately after note was triggered
979     // then immediately apply decay to Note object
980 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
981 schoenebeck 2962 pNote->Override.Decay = fDecay;
982     } else { // otherwise schedule decay change ...
983     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
984     e.Init(); // clear IDs
985     e.Type = Event::type_note_synth_param;
986     e.Param.NoteSynthParam.NoteID = id.noteID();
987     e.Param.NoteSynthParam.Type = Event::synth_param_decay;
988     e.Param.NoteSynthParam.Delta = fDecay;
989     e.Param.NoteSynthParam.Relative = false;
990 schoenebeck 2953
991 schoenebeck 2962 pEngineChannel->ScheduleEventMicroSec(&e, 0);
992     }
993 schoenebeck 2953 }
994     }
995    
996     return successResult();
997     }
998    
999     // change_release() function
1000    
1001     InstrumentScriptVMFunction_change_release::InstrumentScriptVMFunction_change_release(InstrumentScriptVM* parent)
1002     : m_vm(parent)
1003     {
1004     }
1005    
1006     bool InstrumentScriptVMFunction_change_release::acceptsArgType(int iArg, ExprType_t type) const {
1007     if (iArg == 0)
1008     return type == INT_EXPR || type == INT_ARR_EXPR;
1009     else
1010 schoenebeck 3188 return type == INT_EXPR;
1011 schoenebeck 2953 }
1012    
1013     VMFnResult* InstrumentScriptVMFunction_change_release::exec(VMFnArgs* args) {
1014     int release = args->arg(1)->asInt()->evalInt();
1015     if (release > VM_EG_PAR_MAX_VALUE) {
1016     wrnMsg("change_release(): argument 2 may not be larger than 1000000");
1017     release = VM_EG_PAR_MAX_VALUE;
1018     } else if (release < 0) {
1019     wrnMsg("change_release(): argument 2 may not be negative");
1020     release = 0;
1021     }
1022     const float fRelease = float(release) / float(VM_EG_PAR_MAX_VALUE);
1023    
1024     AbstractEngineChannel* pEngineChannel =
1025     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1026    
1027     if (args->arg(0)->exprType() == INT_EXPR) {
1028     const ScriptID id = args->arg(0)->asInt()->evalInt();
1029     if (!id) {
1030     wrnMsg("change_release(): note ID for argument 1 may not be zero");
1031     return successResult();
1032     }
1033     if (!id.isNoteID()) {
1034     wrnMsg("change_release(): argument 1 is not a note ID");
1035     return successResult();
1036     }
1037    
1038     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1039 schoenebeck 2962 if (!pNote) return successResult();
1040 schoenebeck 2953
1041 schoenebeck 2962 // if change_release() was called immediately after note was triggered
1042     // then immediately apply relase to Note object
1043 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1044 schoenebeck 2962 pNote->Override.Release = fRelease;
1045     } else { // otherwise schedule release change ...
1046     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1047     e.Init(); // clear IDs
1048     e.Type = Event::type_note_synth_param;
1049     e.Param.NoteSynthParam.NoteID = id.noteID();
1050     e.Param.NoteSynthParam.Type = Event::synth_param_release;
1051     e.Param.NoteSynthParam.Delta = fRelease;
1052     e.Param.NoteSynthParam.Relative = false;
1053 schoenebeck 2953
1054 schoenebeck 2962 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1055     }
1056 schoenebeck 2953 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1057     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1058     for (int i = 0; i < ids->arraySize(); ++i) {
1059     const ScriptID id = ids->evalIntElement(i);
1060     if (!id || !id.isNoteID()) continue;
1061    
1062     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1063     if (!pNote) continue;
1064    
1065 schoenebeck 2962 // if change_release() was called immediately after note was triggered
1066     // then immediately apply relase to Note object
1067 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1068 schoenebeck 2962 pNote->Override.Release = fRelease;
1069     } else { // otherwise schedule release change ...
1070     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1071     e.Init(); // clear IDs
1072     e.Type = Event::type_note_synth_param;
1073     e.Param.NoteSynthParam.NoteID = id.noteID();
1074     e.Param.NoteSynthParam.Type = Event::synth_param_release;
1075     e.Param.NoteSynthParam.Delta = fRelease;
1076     e.Param.NoteSynthParam.Relative = false;
1077 schoenebeck 2953
1078 schoenebeck 2962 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1079     }
1080 schoenebeck 2953 }
1081     }
1082    
1083     return successResult();
1084     }
1085    
1086 schoenebeck 3246 // template for change_*() functions
1087    
1088 schoenebeck 3118 bool VMChangeSynthParamFunction::acceptsArgType(int iArg, ExprType_t type) const {
1089     if (iArg == 0)
1090     return type == INT_EXPR || type == INT_ARR_EXPR;
1091     else
1092 schoenebeck 3188 return type == INT_EXPR;
1093 schoenebeck 3118 }
1094    
1095 schoenebeck 3193 // Arbitrarily chosen constant value symbolizing "no limit".
1096     #define NO_LIMIT 1315916909
1097    
1098 schoenebeck 3188 template<float NoteBase::_Override::*T_noteParam, int T_synthParam,
1099     bool T_isNormalizedParam, int T_maxValue, int T_minValue>
1100 schoenebeck 3118 VMFnResult* VMChangeSynthParamFunction::execTemplate(VMFnArgs* args, const char* functionName) {
1101     int value = args->arg(1)->asInt()->evalInt();
1102 schoenebeck 3193 if (T_maxValue != NO_LIMIT && value > T_maxValue) {
1103 schoenebeck 3188 wrnMsg(String(functionName) + "(): argument 2 may not be larger than " + ToString(T_maxValue));
1104     value = T_maxValue;
1105 schoenebeck 3193 } else if (T_minValue != NO_LIMIT && value < T_minValue) {
1106 schoenebeck 3188 if (T_minValue == 0)
1107     wrnMsg(String(functionName) + "(): argument 2 may not be negative");
1108     else
1109     wrnMsg(String(functionName) + "(): argument 2 may not be smaller than " + ToString(T_minValue));
1110     value = T_minValue;
1111 schoenebeck 3118 }
1112 schoenebeck 3188 const float fValue = (T_isNormalizedParam) ?
1113     float(value) / float(T_maxValue) : // convert to 0.0 .. 1.0 value range
1114     float(value) / 1000000.f; // assuming microseconds here, convert to seconds
1115 schoenebeck 3118
1116     AbstractEngineChannel* pEngineChannel =
1117     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1118    
1119     if (args->arg(0)->exprType() == INT_EXPR) {
1120     const ScriptID id = args->arg(0)->asInt()->evalInt();
1121     if (!id) {
1122     wrnMsg(String(functionName) + "(): note ID for argument 1 may not be zero");
1123     return successResult();
1124     }
1125     if (!id.isNoteID()) {
1126     wrnMsg(String(functionName) + "(): argument 1 is not a note ID");
1127     return successResult();
1128     }
1129    
1130     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1131     if (!pNote) return successResult();
1132    
1133     // if this change_*() script function was called immediately after
1134     // note was triggered then immediately apply the synth parameter
1135     // change to Note object
1136 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1137 schoenebeck 3118 pNote->Override.*T_noteParam = fValue;
1138     } else { // otherwise schedule this synth parameter change ...
1139     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1140     e.Init(); // clear IDs
1141     e.Type = Event::type_note_synth_param;
1142     e.Param.NoteSynthParam.NoteID = id.noteID();
1143     e.Param.NoteSynthParam.Type = (Event::synth_param_t) T_synthParam;
1144     e.Param.NoteSynthParam.Delta = fValue;
1145     e.Param.NoteSynthParam.Relative = false;
1146    
1147     pEngineChannel->ScheduleEventMicroSec(&e, 0);
1148     }
1149     } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1150     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1151     for (int i = 0; i < ids->arraySize(); ++i) {
1152     const ScriptID id = ids->evalIntElement(i);
1153     if (!id || !id.isNoteID()) continue;
1154    
1155     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1156     if (!pNote) continue;
1157    
1158     // if this change_*() script function was called immediately after
1159     // note was triggered then immediately apply the synth parameter
1160     // change to Note object
1161 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1162 schoenebeck 3118 pNote->Override.*T_noteParam = fValue;
1163     } else { // otherwise schedule this synth parameter change ...
1164     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1165     e.Init(); // clear IDs
1166     e.Type = Event::type_note_synth_param;
1167     e.Param.NoteSynthParam.NoteID = id.noteID();
1168     e.Param.NoteSynthParam.Type = (Event::synth_param_t) T_synthParam;
1169     e.Param.NoteSynthParam.Delta = fValue;
1170     e.Param.NoteSynthParam.Relative = false;
1171    
1172     pEngineChannel->ScheduleEventMicroSec(&e, 0);
1173     }
1174     }
1175     }
1176    
1177     return successResult();
1178     }
1179    
1180     // change_amp_lfo_depth() function
1181    
1182     VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_depth::exec(VMFnArgs* args) {
1183 schoenebeck 3188 return VMChangeSynthParamFunction::execTemplate<
1184     &NoteBase::_Override::AmpLFODepth,
1185 schoenebeck 3193 Event::synth_param_amp_lfo_depth,
1186     true, 1000000, 0>( args, "change_amp_lfo_depth" );
1187 schoenebeck 3118 }
1188    
1189     // change_amp_lfo_freq() function
1190    
1191     VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_freq::exec(VMFnArgs* args) {
1192 schoenebeck 3188 return VMChangeSynthParamFunction::execTemplate<
1193     &NoteBase::_Override::AmpLFOFreq,
1194 schoenebeck 3193 Event::synth_param_amp_lfo_freq,
1195     true, 1000000, 0>( args, "change_amp_lfo_freq" );
1196 schoenebeck 3118 }
1197    
1198     // change_pitch_lfo_depth() function
1199    
1200     VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_depth::exec(VMFnArgs* args) {
1201 schoenebeck 3188 return VMChangeSynthParamFunction::execTemplate<
1202     &NoteBase::_Override::PitchLFODepth,
1203 schoenebeck 3193 Event::synth_param_pitch_lfo_depth,
1204     true, 1000000, 0>( args, "change_pitch_lfo_depth" );
1205 schoenebeck 3118 }
1206    
1207     // change_pitch_lfo_freq() function
1208    
1209     VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_freq::exec(VMFnArgs* args) {
1210 schoenebeck 3188 return VMChangeSynthParamFunction::execTemplate<
1211     &NoteBase::_Override::PitchLFOFreq,
1212 schoenebeck 3193 Event::synth_param_pitch_lfo_freq,
1213     true, 1000000, 0>( args, "change_pitch_lfo_freq" );
1214 schoenebeck 3118 }
1215    
1216 schoenebeck 3188 // change_vol_time() function
1217    
1218     VMFnResult* InstrumentScriptVMFunction_change_vol_time::exec(VMFnArgs* args) {
1219     return VMChangeSynthParamFunction::execTemplate<
1220     &NoteBase::_Override::VolumeTime,
1221 schoenebeck 3193 Event::synth_param_volume_time,
1222     false, NO_LIMIT, 0>( args, "change_vol_time" );
1223 schoenebeck 3188 }
1224    
1225     // change_tune_time() function
1226    
1227     VMFnResult* InstrumentScriptVMFunction_change_tune_time::exec(VMFnArgs* args) {
1228     return VMChangeSynthParamFunction::execTemplate<
1229     &NoteBase::_Override::PitchTime,
1230 schoenebeck 3193 Event::synth_param_pitch_time,
1231     false, NO_LIMIT, 0>( args, "change_tune_time" );
1232 schoenebeck 3188 }
1233    
1234 schoenebeck 3246 // template for change_*_curve() functions
1235    
1236     bool VMChangeFadeCurveFunction::acceptsArgType(int iArg, ExprType_t type) const {
1237     if (iArg == 0)
1238     return type == INT_EXPR || type == INT_ARR_EXPR;
1239     else
1240     return type == INT_EXPR;
1241     }
1242    
1243     template<fade_curve_t NoteBase::_Override::*T_noteParam, int T_synthParam>
1244     VMFnResult* VMChangeFadeCurveFunction::execTemplate(VMFnArgs* args, const char* functionName) {
1245     int value = args->arg(1)->asInt()->evalInt();
1246     switch (value) {
1247     case FADE_CURVE_LINEAR:
1248     case FADE_CURVE_EASE_IN_EASE_OUT:
1249     break;
1250     default:
1251     wrnMsg(String(functionName) + "(): invalid curve type passed as argument 2");
1252     return successResult();
1253     }
1254    
1255     AbstractEngineChannel* pEngineChannel =
1256     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1257    
1258     if (args->arg(0)->exprType() == INT_EXPR) {
1259     const ScriptID id = args->arg(0)->asInt()->evalInt();
1260     if (!id) {
1261     wrnMsg(String(functionName) + "(): note ID for argument 1 may not be zero");
1262     return successResult();
1263     }
1264     if (!id.isNoteID()) {
1265     wrnMsg(String(functionName) + "(): argument 1 is not a note ID");
1266     return successResult();
1267     }
1268    
1269     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1270     if (!pNote) return successResult();
1271    
1272     // if this change_*_curve() script function was called immediately after
1273     // note was triggered then immediately apply the synth parameter
1274     // change to Note object
1275     if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1276     pNote->Override.*T_noteParam = (fade_curve_t) value;
1277     } else { // otherwise schedule this synth parameter change ...
1278     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1279     e.Init(); // clear IDs
1280     e.Type = Event::type_note_synth_param;
1281     e.Param.NoteSynthParam.NoteID = id.noteID();
1282     e.Param.NoteSynthParam.Type = (Event::synth_param_t) T_synthParam;
1283     e.Param.NoteSynthParam.Delta = value;
1284     e.Param.NoteSynthParam.Relative = false;
1285    
1286     pEngineChannel->ScheduleEventMicroSec(&e, 0);
1287     }
1288     } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1289     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1290     for (int i = 0; i < ids->arraySize(); ++i) {
1291     const ScriptID id = ids->evalIntElement(i);
1292     if (!id || !id.isNoteID()) continue;
1293    
1294     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1295     if (!pNote) continue;
1296    
1297     // if this change_*_curve() script function was called immediately after
1298     // note was triggered then immediately apply the synth parameter
1299     // change to Note object
1300     if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1301     pNote->Override.*T_noteParam = (fade_curve_t) value;
1302     } else { // otherwise schedule this synth parameter change ...
1303     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1304     e.Init(); // clear IDs
1305     e.Type = Event::type_note_synth_param;
1306     e.Param.NoteSynthParam.NoteID = id.noteID();
1307     e.Param.NoteSynthParam.Type = (Event::synth_param_t) T_synthParam;
1308     e.Param.NoteSynthParam.Delta = value;
1309     e.Param.NoteSynthParam.Relative = false;
1310    
1311     pEngineChannel->ScheduleEventMicroSec(&e, 0);
1312     }
1313     }
1314     }
1315    
1316     return successResult();
1317     }
1318    
1319     // change_vol_curve() function
1320    
1321     VMFnResult* InstrumentScriptVMFunction_change_vol_curve::exec(VMFnArgs* args) {
1322     return VMChangeFadeCurveFunction::execTemplate<
1323     &NoteBase::_Override::VolumeCurve,
1324     Event::synth_param_volume_curve>( args, "change_vol_curve" );
1325     }
1326    
1327     // change_tune_curve() function
1328    
1329     VMFnResult* InstrumentScriptVMFunction_change_tune_curve::exec(VMFnArgs* args) {
1330     return VMChangeFadeCurveFunction::execTemplate<
1331     &NoteBase::_Override::PitchCurve,
1332     Event::synth_param_pitch_curve>( args, "change_tune_curve" );
1333     }
1334    
1335 schoenebeck 3188 // fade_in() function
1336    
1337     InstrumentScriptVMFunction_fade_in::InstrumentScriptVMFunction_fade_in(InstrumentScriptVM* parent)
1338     : m_vm(parent)
1339     {
1340     }
1341    
1342     bool InstrumentScriptVMFunction_fade_in::acceptsArgType(int iArg, ExprType_t type) const {
1343     if (iArg == 0)
1344     return type == INT_EXPR || type == INT_ARR_EXPR;
1345     else
1346     return type == INT_EXPR;
1347     }
1348    
1349     VMFnResult* InstrumentScriptVMFunction_fade_in::exec(VMFnArgs* args) {
1350     int duration = args->arg(1)->asInt()->evalInt();
1351     if (duration < 0) {
1352     wrnMsg("fade_in(): argument 2 may not be negative");
1353     duration = 0;
1354     }
1355     const float fDuration = float(duration) / 1000000.f; // convert microseconds to seconds
1356    
1357     AbstractEngineChannel* pEngineChannel =
1358     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1359    
1360     if (args->arg(0)->exprType() == INT_EXPR) {
1361     const ScriptID id = args->arg(0)->asInt()->evalInt();
1362     if (!id) {
1363     wrnMsg("fade_in(): note ID for argument 1 may not be zero");
1364     return successResult();
1365     }
1366     if (!id.isNoteID()) {
1367     wrnMsg("fade_in(): argument 1 is not a note ID");
1368     return successResult();
1369     }
1370    
1371     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1372     if (!pNote) return successResult();
1373    
1374     // if fade_in() was called immediately after note was triggered
1375     // then immediately apply a start volume of zero to Note object,
1376     // as well as the fade in duration
1377 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1378 schoenebeck 3188 pNote->Override.Volume = 0.f;
1379     pNote->Override.VolumeTime = fDuration;
1380     } else { // otherwise schedule a "volume time" change with the requested fade in duration ...
1381     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1382     e.Init(); // clear IDs
1383     e.Type = Event::type_note_synth_param;
1384     e.Param.NoteSynthParam.NoteID = id.noteID();
1385     e.Param.NoteSynthParam.Type = Event::synth_param_volume_time;
1386     e.Param.NoteSynthParam.Delta = fDuration;
1387     e.Param.NoteSynthParam.Relative = false;
1388    
1389     pEngineChannel->ScheduleEventMicroSec(&e, 0);
1390     }
1391     // and finally schedule a "volume" change, simply one time slice
1392     // ahead, with the final fade in volume (1.0)
1393     {
1394     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1395     e.Init(); // clear IDs
1396     e.Type = Event::type_note_synth_param;
1397     e.Param.NoteSynthParam.NoteID = id.noteID();
1398     e.Param.NoteSynthParam.Type = Event::synth_param_volume;
1399     e.Param.NoteSynthParam.Delta = 1.f;
1400     e.Param.NoteSynthParam.Relative = false;
1401    
1402     // scheduling with 0 delay would also work here, but +1 is more
1403     // safe regarding potential future implementation changes of the
1404     // scheduler (see API comments of RTAVLTree::insert())
1405     pEngineChannel->ScheduleEventMicroSec(&e, 1);
1406     }
1407     } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1408     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1409     for (int i = 0; i < ids->arraySize(); ++i) {
1410     const ScriptID id = ids->evalIntElement(i);
1411     if (!id || !id.isNoteID()) continue;
1412    
1413     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1414     if (!pNote) continue;
1415    
1416     // if fade_in() was called immediately after note was triggered
1417     // then immediately apply a start volume of zero to Note object,
1418     // as well as the fade in duration
1419 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1420 schoenebeck 3188 pNote->Override.Volume = 0.f;
1421     pNote->Override.VolumeTime = fDuration;
1422     } else { // otherwise schedule a "volume time" change with the requested fade in duration ...
1423     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1424     e.Init(); // clear IDs
1425     e.Type = Event::type_note_synth_param;
1426     e.Param.NoteSynthParam.NoteID = id.noteID();
1427     e.Param.NoteSynthParam.Type = Event::synth_param_volume_time;
1428     e.Param.NoteSynthParam.Delta = fDuration;
1429     e.Param.NoteSynthParam.Relative = false;
1430    
1431     pEngineChannel->ScheduleEventMicroSec(&e, 0);
1432     }
1433     // and finally schedule a "volume" change, simply one time slice
1434     // ahead, with the final fade in volume (1.0)
1435     {
1436     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1437     e.Init(); // clear IDs
1438     e.Type = Event::type_note_synth_param;
1439     e.Param.NoteSynthParam.NoteID = id.noteID();
1440     e.Param.NoteSynthParam.Type = Event::synth_param_volume;
1441     e.Param.NoteSynthParam.Delta = 1.f;
1442     e.Param.NoteSynthParam.Relative = false;
1443    
1444     // scheduling with 0 delay would also work here, but +1 is more
1445     // safe regarding potential future implementation changes of the
1446     // scheduler (see API comments of RTAVLTree::insert())
1447     pEngineChannel->ScheduleEventMicroSec(&e, 1);
1448     }
1449     }
1450     }
1451    
1452     return successResult();
1453     }
1454    
1455     // fade_out() function
1456    
1457     InstrumentScriptVMFunction_fade_out::InstrumentScriptVMFunction_fade_out(InstrumentScriptVM* parent)
1458     : m_vm(parent)
1459     {
1460     }
1461    
1462     bool InstrumentScriptVMFunction_fade_out::acceptsArgType(int iArg, ExprType_t type) const {
1463     if (iArg == 0)
1464     return type == INT_EXPR || type == INT_ARR_EXPR;
1465     else
1466     return type == INT_EXPR;
1467     }
1468    
1469     VMFnResult* InstrumentScriptVMFunction_fade_out::exec(VMFnArgs* args) {
1470     int duration = args->arg(1)->asInt()->evalInt();
1471     if (duration < 0) {
1472     wrnMsg("fade_out(): argument 2 may not be negative");
1473     duration = 0;
1474     }
1475     const float fDuration = float(duration) / 1000000.f; // convert microseconds to seconds
1476    
1477     bool stop = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : true;
1478    
1479     AbstractEngineChannel* pEngineChannel =
1480     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1481    
1482     if (args->arg(0)->exprType() == INT_EXPR) {
1483     const ScriptID id = args->arg(0)->asInt()->evalInt();
1484     if (!id) {
1485     wrnMsg("fade_out(): note ID for argument 1 may not be zero");
1486     return successResult();
1487     }
1488     if (!id.isNoteID()) {
1489     wrnMsg("fade_out(): argument 1 is not a note ID");
1490     return successResult();
1491     }
1492    
1493     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1494     if (!pNote) return successResult();
1495    
1496     // if fade_out() was called immediately after note was triggered
1497     // then immediately apply fade out duration to Note object
1498 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1499 schoenebeck 3188 pNote->Override.VolumeTime = fDuration;
1500     } else { // otherwise schedule a "volume time" change with the requested fade out duration ...
1501     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1502     e.Init(); // clear IDs
1503     e.Type = Event::type_note_synth_param;
1504     e.Param.NoteSynthParam.NoteID = id.noteID();
1505     e.Param.NoteSynthParam.Type = Event::synth_param_volume_time;
1506     e.Param.NoteSynthParam.Delta = fDuration;
1507     e.Param.NoteSynthParam.Relative = false;
1508    
1509     pEngineChannel->ScheduleEventMicroSec(&e, 0);
1510     }
1511     // now schedule a "volume" change, simply one time slice ahead, with
1512     // the final fade out volume (0.0)
1513     {
1514     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1515     e.Init(); // clear IDs
1516     e.Type = Event::type_note_synth_param;
1517     e.Param.NoteSynthParam.NoteID = id.noteID();
1518     e.Param.NoteSynthParam.Type = Event::synth_param_volume;
1519     e.Param.NoteSynthParam.Delta = 0.f;
1520     e.Param.NoteSynthParam.Relative = false;
1521    
1522     // scheduling with 0 delay would also work here, but +1 is more
1523     // safe regarding potential future implementation changes of the
1524     // scheduler (see API comments of RTAVLTree::insert())
1525     pEngineChannel->ScheduleEventMicroSec(&e, 1);
1526     }
1527     // and finally if stopping the note was requested after the fade out
1528     // completed, then schedule to kill the voice after the requested
1529     // duration
1530     if (stop) {
1531     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1532     e.Init(); // clear IDs
1533     e.Type = Event::type_kill_note;
1534     e.Param.Note.ID = id.noteID();
1535     e.Param.Note.Key = pNote->hostKey;
1536    
1537     pEngineChannel->ScheduleEventMicroSec(&e, duration + 1);
1538     }
1539     } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1540     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1541     for (int i = 0; i < ids->arraySize(); ++i) {
1542     const ScriptID id = ids->evalIntElement(i);
1543     if (!id || !id.isNoteID()) continue;
1544    
1545     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1546     if (!pNote) continue;
1547    
1548     // if fade_out() was called immediately after note was triggered
1549     // then immediately apply fade out duration to Note object
1550 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1551 schoenebeck 3188 pNote->Override.VolumeTime = fDuration;
1552     } else { // otherwise schedule a "volume time" change with the requested fade out duration ...
1553     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1554     e.Init(); // clear IDs
1555     e.Type = Event::type_note_synth_param;
1556     e.Param.NoteSynthParam.NoteID = id.noteID();
1557     e.Param.NoteSynthParam.Type = Event::synth_param_volume_time;
1558     e.Param.NoteSynthParam.Delta = fDuration;
1559     e.Param.NoteSynthParam.Relative = false;
1560    
1561     pEngineChannel->ScheduleEventMicroSec(&e, 0);
1562     }
1563     // now schedule a "volume" change, simply one time slice ahead, with
1564     // the final fade out volume (0.0)
1565     {
1566     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1567     e.Init(); // clear IDs
1568     e.Type = Event::type_note_synth_param;
1569     e.Param.NoteSynthParam.NoteID = id.noteID();
1570     e.Param.NoteSynthParam.Type = Event::synth_param_volume;
1571     e.Param.NoteSynthParam.Delta = 0.f;
1572     e.Param.NoteSynthParam.Relative = false;
1573    
1574     // scheduling with 0 delay would also work here, but +1 is more
1575     // safe regarding potential future implementation changes of the
1576     // scheduler (see API comments of RTAVLTree::insert())
1577     pEngineChannel->ScheduleEventMicroSec(&e, 1);
1578     }
1579     // and finally if stopping the note was requested after the fade out
1580     // completed, then schedule to kill the voice after the requested
1581     // duration
1582     if (stop) {
1583     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1584     e.Init(); // clear IDs
1585     e.Type = Event::type_kill_note;
1586     e.Param.Note.ID = id.noteID();
1587     e.Param.Note.Key = pNote->hostKey;
1588    
1589     pEngineChannel->ScheduleEventMicroSec(&e, duration + 1);
1590     }
1591     }
1592     }
1593    
1594     return successResult();
1595     }
1596    
1597 schoenebeck 3193 // get_event_par() function
1598    
1599     InstrumentScriptVMFunction_get_event_par::InstrumentScriptVMFunction_get_event_par(InstrumentScriptVM* parent)
1600     : m_vm(parent)
1601     {
1602     }
1603    
1604     VMFnResult* InstrumentScriptVMFunction_get_event_par::exec(VMFnArgs* args) {
1605     AbstractEngineChannel* pEngineChannel =
1606     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1607    
1608     const ScriptID id = args->arg(0)->asInt()->evalInt();
1609     if (!id) {
1610     wrnMsg("get_event_par(): note ID for argument 1 may not be zero");
1611     return successResult(0);
1612     }
1613     if (!id.isNoteID()) {
1614     wrnMsg("get_event_par(): argument 1 is not a note ID");
1615     return successResult(0);
1616     }
1617    
1618     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1619     if (!pNote) {
1620     wrnMsg("get_event_par(): no note alive with that note ID of argument 1");
1621     return successResult(0);
1622     }
1623    
1624     const int parameter = args->arg(1)->asInt()->evalInt();
1625     switch (parameter) {
1626     case EVENT_PAR_NOTE:
1627     return successResult(pNote->cause.Param.Note.Key);
1628     case EVENT_PAR_VELOCITY:
1629     return successResult(pNote->cause.Param.Note.Velocity);
1630     case EVENT_PAR_VOLUME:
1631     return successResult(
1632     RTMath::LinRatioToDecibel(pNote->Override.Volume) * 1000.f
1633     );
1634     case EVENT_PAR_TUNE:
1635     return successResult(
1636     RTMath::FreqRatioToCents(pNote->Override.Pitch) * 1000.f
1637     );
1638     case EVENT_PAR_0:
1639     return successResult(pNote->userPar[0]);
1640     case EVENT_PAR_1:
1641     return successResult(pNote->userPar[1]);
1642     case EVENT_PAR_2:
1643     return successResult(pNote->userPar[2]);
1644     case EVENT_PAR_3:
1645     return successResult(pNote->userPar[3]);
1646     }
1647    
1648     wrnMsg("get_event_par(): argument 2 is an invalid event parameter");
1649     return successResult(0);
1650     }
1651    
1652     // set_event_par() function
1653    
1654     InstrumentScriptVMFunction_set_event_par::InstrumentScriptVMFunction_set_event_par(InstrumentScriptVM* parent)
1655     : m_vm(parent)
1656     {
1657     }
1658    
1659     VMFnResult* InstrumentScriptVMFunction_set_event_par::exec(VMFnArgs* args) {
1660     AbstractEngineChannel* pEngineChannel =
1661     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1662    
1663     const ScriptID id = args->arg(0)->asInt()->evalInt();
1664     if (!id) {
1665     wrnMsg("set_event_par(): note ID for argument 1 may not be zero");
1666     return successResult();
1667     }
1668     if (!id.isNoteID()) {
1669     wrnMsg("set_event_par(): argument 1 is not a note ID");
1670     return successResult();
1671     }
1672    
1673     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1674     if (!pNote) return successResult();
1675    
1676     const int parameter = args->arg(1)->asInt()->evalInt();
1677     const int value = args->arg(2)->asInt()->evalInt();
1678    
1679     switch (parameter) {
1680     case EVENT_PAR_NOTE:
1681     if (value < 0 || value > 127) {
1682     wrnMsg("set_event_par(): note number of argument 3 is out of range");
1683     return successResult();
1684     }
1685 schoenebeck 3214 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1686 schoenebeck 3193 pNote->cause.Param.Note.Key = value;
1687 schoenebeck 3214 m_vm->m_event->cause.Param.Note.Key = value;
1688     } else {
1689 schoenebeck 3193 wrnMsg("set_event_par(): note number can only be changed when note is new");
1690 schoenebeck 3214 }
1691 schoenebeck 3193 return successResult();
1692     case EVENT_PAR_VELOCITY:
1693     if (value < 0 || value > 127) {
1694     wrnMsg("set_event_par(): velocity of argument 3 is out of range");
1695     return successResult();
1696     }
1697 schoenebeck 3214 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1698 schoenebeck 3193 pNote->cause.Param.Note.Velocity = value;
1699 schoenebeck 3214 m_vm->m_event->cause.Param.Note.Velocity = value;
1700     } else {
1701 schoenebeck 3193 wrnMsg("set_event_par(): velocity can only be changed when note is new");
1702 schoenebeck 3214 }
1703 schoenebeck 3193 return successResult();
1704     case EVENT_PAR_VOLUME:
1705     wrnMsg("set_event_par(): changing volume by this function is currently not supported, use change_vol() instead");
1706     return successResult();
1707     case EVENT_PAR_TUNE:
1708     wrnMsg("set_event_par(): changing tune by this function is currently not supported, use change_tune() instead");
1709     return successResult();
1710     case EVENT_PAR_0:
1711     pNote->userPar[0] = value;
1712     return successResult();
1713     case EVENT_PAR_1:
1714     pNote->userPar[1] = value;
1715     return successResult();
1716     case EVENT_PAR_2:
1717     pNote->userPar[2] = value;
1718     return successResult();
1719     case EVENT_PAR_3:
1720     pNote->userPar[3] = value;
1721     return successResult();
1722     }
1723    
1724     wrnMsg("set_event_par(): argument 2 is an invalid event parameter");
1725     return successResult();
1726     }
1727    
1728 schoenebeck 3214 // change_note() function
1729    
1730     InstrumentScriptVMFunction_change_note::InstrumentScriptVMFunction_change_note(InstrumentScriptVM* parent)
1731     : m_vm(parent)
1732     {
1733     }
1734    
1735     VMFnResult* InstrumentScriptVMFunction_change_note::exec(VMFnArgs* args) {
1736     AbstractEngineChannel* pEngineChannel =
1737     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1738    
1739     const ScriptID id = args->arg(0)->asInt()->evalInt();
1740     if (!id) {
1741     wrnMsg("change_note(): note ID for argument 1 may not be zero");
1742     return successResult();
1743     }
1744     if (!id.isNoteID()) {
1745     wrnMsg("change_note(): argument 1 is not a note ID");
1746     return successResult();
1747     }
1748    
1749     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1750     if (!pNote) return successResult();
1751    
1752     const int value = args->arg(1)->asInt()->evalInt();
1753     if (value < 0 || value > 127) {
1754     wrnMsg("change_note(): note number of argument 2 is out of range");
1755     return successResult();
1756     }
1757    
1758     if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1759     pNote->cause.Param.Note.Key = value;
1760     m_vm->m_event->cause.Param.Note.Key = value;
1761 schoenebeck 3216 } else {
1762 schoenebeck 3214 wrnMsg("change_note(): note number can only be changed when note is new");
1763     }
1764    
1765     return successResult();
1766     }
1767    
1768     // change_velo() function
1769    
1770     InstrumentScriptVMFunction_change_velo::InstrumentScriptVMFunction_change_velo(InstrumentScriptVM* parent)
1771     : m_vm(parent)
1772     {
1773     }
1774    
1775     VMFnResult* InstrumentScriptVMFunction_change_velo::exec(VMFnArgs* args) {
1776     AbstractEngineChannel* pEngineChannel =
1777     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1778    
1779     const ScriptID id = args->arg(0)->asInt()->evalInt();
1780     if (!id) {
1781     wrnMsg("change_velo(): note ID for argument 1 may not be zero");
1782     return successResult();
1783     }
1784     if (!id.isNoteID()) {
1785     wrnMsg("change_velo(): argument 1 is not a note ID");
1786     return successResult();
1787     }
1788    
1789     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1790     if (!pNote) return successResult();
1791    
1792     const int value = args->arg(1)->asInt()->evalInt();
1793     if (value < 0 || value > 127) {
1794     wrnMsg("change_velo(): velocity of argument 2 is out of range");
1795     return successResult();
1796     }
1797    
1798     if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1799     pNote->cause.Param.Note.Velocity = value;
1800     m_vm->m_event->cause.Param.Note.Velocity = value;
1801 schoenebeck 3216 } else {
1802 schoenebeck 3214 wrnMsg("change_velo(): velocity can only be changed when note is new");
1803     }
1804    
1805     return successResult();
1806     }
1807    
1808 schoenebeck 3255 // change_play_pos() function
1809    
1810     InstrumentScriptVMFunction_change_play_pos::InstrumentScriptVMFunction_change_play_pos(InstrumentScriptVM* parent)
1811     : m_vm(parent)
1812     {
1813     }
1814    
1815     VMFnResult* InstrumentScriptVMFunction_change_play_pos::exec(VMFnArgs* args) {
1816     const ScriptID id = args->arg(0)->asInt()->evalInt();
1817     if (!id) {
1818     wrnMsg("change_play_pos(): note ID for argument 1 may not be zero");
1819     return successResult();
1820     }
1821     if (!id.isNoteID()) {
1822     wrnMsg("change_play_pos(): argument 1 is not a note ID");
1823     return successResult();
1824     }
1825    
1826     const int pos = args->arg(1)->asInt()->evalInt();
1827     if (pos < 0) {
1828     wrnMsg("change_play_pos(): playback position of argument 2 may not be negative");
1829     return successResult();
1830     }
1831    
1832     AbstractEngineChannel* pEngineChannel =
1833     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1834    
1835     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1836     if (!pNote) return successResult();
1837    
1838     pNote->Override.SampleOffset = pos;
1839    
1840     return successResult();
1841     }
1842    
1843 schoenebeck 2935 // event_status() function
1844    
1845     InstrumentScriptVMFunction_event_status::InstrumentScriptVMFunction_event_status(InstrumentScriptVM* parent)
1846     : m_vm(parent)
1847     {
1848     }
1849    
1850     VMFnResult* InstrumentScriptVMFunction_event_status::exec(VMFnArgs* args) {
1851     AbstractEngineChannel* pEngineChannel =
1852     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1853    
1854     const ScriptID id = args->arg(0)->asInt()->evalInt();
1855     if (!id) {
1856     wrnMsg("event_status(): note ID for argument 1 may not be zero");
1857     return successResult(EVENT_STATUS_INACTIVE);
1858     }
1859     if (!id.isNoteID()) {
1860     wrnMsg("event_status(): argument 1 is not a note ID");
1861     return successResult(EVENT_STATUS_INACTIVE);
1862     }
1863    
1864     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1865     return successResult(pNote ? EVENT_STATUS_NOTE_QUEUE : EVENT_STATUS_INACTIVE);
1866     }
1867    
1868 schoenebeck 2948 // wait() function (overrides core wait() implementation)
1869    
1870     InstrumentScriptVMFunction_wait::InstrumentScriptVMFunction_wait(InstrumentScriptVM* parent)
1871     : CoreVMFunction_wait(parent)
1872     {
1873     }
1874    
1875     VMFnResult* InstrumentScriptVMFunction_wait::exec(VMFnArgs* args) {
1876     InstrumentScriptVM* m_vm = (InstrumentScriptVM*) vm;
1877    
1878     // this might be set by passing 1 with the 2nd argument of built-in stop_wait() function
1879     if (m_vm->m_event->ignoreAllWaitCalls) return successResult();
1880    
1881     return CoreVMFunction_wait::exec(args);
1882     }
1883    
1884     // stop_wait() function
1885    
1886     InstrumentScriptVMFunction_stop_wait::InstrumentScriptVMFunction_stop_wait(InstrumentScriptVM* parent)
1887     : m_vm(parent)
1888     {
1889     }
1890    
1891     VMFnResult* InstrumentScriptVMFunction_stop_wait::exec(VMFnArgs* args) {
1892     AbstractEngineChannel* pEngineChannel =
1893     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1894    
1895     const script_callback_id_t id = args->arg(0)->asInt()->evalInt();
1896     if (!id) {
1897     wrnMsg("stop_wait(): callback ID for argument 1 may not be zero");
1898     return successResult();
1899     }
1900    
1901     RTList<ScriptEvent>::Iterator itCallback = pEngineChannel->ScriptCallbackByID(id);
1902     if (!itCallback) return successResult(); // ignore if callback is i.e. not alive anymore
1903    
1904     const bool disableWaitForever =
1905     (args->argsCount() >= 2) ? (args->arg(1)->asInt()->evalInt() == 1) : false;
1906    
1907     pEngineChannel->ScheduleResumeOfScriptCallback(
1908 schoenebeck 3205 itCallback, m_vm->m_event->scheduleTime, disableWaitForever
1909 schoenebeck 2948 );
1910    
1911     return successResult();
1912     }
1913    
1914 schoenebeck 3277 // abort() function
1915    
1916     InstrumentScriptVMFunction_abort::InstrumentScriptVMFunction_abort(InstrumentScriptVM* parent)
1917 schoenebeck 3293 : m_vm(parent)
1918 schoenebeck 3277 {
1919     }
1920    
1921     VMFnResult* InstrumentScriptVMFunction_abort::exec(VMFnArgs* args) {
1922     const script_callback_id_t id = args->arg(0)->asInt()->evalInt();
1923     if (!id) {
1924     wrnMsg("abort(): callback ID for argument 1 may not be zero");
1925     return successResult();
1926     }
1927    
1928     AbstractEngineChannel* pEngineChannel =
1929     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1930    
1931     RTList<ScriptEvent>::Iterator itCallback = pEngineChannel->ScriptCallbackByID(id);
1932     if (!itCallback) return successResult(); // ignore if callback is i.e. not alive anymore
1933    
1934     itCallback->execCtx->signalAbort();
1935    
1936     return successResult();
1937     }
1938    
1939 schoenebeck 3293 // fork() function
1940    
1941     InstrumentScriptVMFunction_fork::InstrumentScriptVMFunction_fork(InstrumentScriptVM* parent)
1942     : m_vm(parent)
1943     {
1944     }
1945    
1946     VMFnResult* InstrumentScriptVMFunction_fork::exec(VMFnArgs* args) {
1947     // check if this is actually the parent going to fork, or rather one of
1948     // the children which is already forked
1949     if (m_vm->m_event->forkIndex != 0) { // this is the entry point for a child ...
1950     int forkResult = m_vm->m_event->forkIndex;
1951     // reset so that this child may i.e. also call fork() later on
1952     m_vm->m_event->forkIndex = 0;
1953     return successResult(forkResult);
1954     }
1955    
1956     // if we are here, then this is the parent, so we must fork this parent
1957    
1958     const int n =
1959     (args->argsCount() >= 1) ? args->arg(0)->asInt()->evalInt() : 1;
1960     const bool bAutoAbort =
1961     (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : true;
1962    
1963     if (m_vm->m_event->countChildHandlers() + n > MAX_FORK_PER_SCRIPT_HANDLER) {
1964     wrnMsg("fork(): requested amount would exceed allowed limit per event handler");
1965     return successResult(-1);
1966     }
1967    
1968     AbstractEngineChannel* pEngineChannel =
1969     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1970    
1971     if (!pEngineChannel->hasFreeScriptCallbacks(n)) {
1972     wrnMsg("fork(): global limit of event handlers exceeded");
1973     return successResult(-1);
1974     }
1975    
1976     for (int iChild = 0; iChild < n; ++iChild) {
1977     RTList<ScriptEvent>::Iterator itChild =
1978     pEngineChannel->forkScriptCallback(m_vm->m_event, bAutoAbort);
1979     if (!itChild) { // should never happen, otherwise its a bug ...
1980     errMsg("fork(): internal error while allocating child");
1981     return errorResult(-1); // terminate script
1982     }
1983     // since both parent, as well all child script execution instances
1984     // all land in this exect() method, the following is (more or less)
1985     // the only feature that lets us distinguish the parent and
1986     // respective children from each other in this exect() method
1987     itChild->forkIndex = iChild + 1;
1988     }
1989    
1990     return successResult(0);
1991     }
1992    
1993 schoenebeck 2596 } // namespace LinuxSampler

  ViewVC Help
Powered by ViewVC