/[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 3216 - (hide annotations) (download)
Thu May 25 15:16:28 2017 UTC (9 months ago) by schoenebeck
File size: 75730 byte(s)
- Fixed compilation error with some compilers.

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     int sampleoffset = (args->argsCount() >= 3) ? args->arg(2)->asInt()->evalInt() : 0;
27 schoenebeck 2879 int duration = (args->argsCount() >= 4) ? args->arg(3)->asInt()->evalInt() : 0; //TODO: -1 might be a better default value instead of 0
28 schoenebeck 2596
29     if (note < 0 || note > 127) {
30     errMsg("play_note(): argument 1 is an invalid note number");
31 schoenebeck 2879 return errorResult(0);
32 schoenebeck 2596 }
33    
34     if (velocity < 0 || velocity > 127) {
35     errMsg("play_note(): argument 2 is an invalid velocity value");
36 schoenebeck 2879 return errorResult(0);
37 schoenebeck 2596 }
38    
39     if (sampleoffset < 0) {
40     errMsg("play_note(): argument 3 may not be a negative sample offset");
41 schoenebeck 2879 return errorResult(0);
42 schoenebeck 2596 } else if (sampleoffset != 0) {
43     wrnMsg("play_note(): argument 3 does not support a sample offset other than 0 yet");
44     }
45    
46     if (duration < -1) {
47     errMsg("play_note(): argument 4 must be a duration value of at least -1 or higher");
48 schoenebeck 2879 return errorResult(0);
49 schoenebeck 2596 }
50    
51     AbstractEngineChannel* pEngineChannel =
52     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
53    
54 schoenebeck 2879 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
55     e.Init(); // clear IDs
56 schoenebeck 2938 e.Type = Event::type_play_note;
57 schoenebeck 2596 e.Param.Note.Key = note;
58     e.Param.Note.Velocity = velocity;
59 schoenebeck 2879 // make this new note dependent to the life time of the original note
60     if (duration == -1) {
61     if (m_vm->currentVMEventHandler()->eventHandlerType() != VM_EVENT_HANDLER_NOTE) {
62     errMsg("play_note(): -1 for argument 4 may only be used for note event handlers");
63     return errorResult(0);
64     }
65     e.Param.Note.ParentNoteID = m_vm->m_event->cause.Param.Note.ID;
66     }
67 schoenebeck 2596
68 schoenebeck 2879 const note_id_t id = pEngineChannel->ScheduleNoteMicroSec(&e, 0);
69 schoenebeck 2596
70 schoenebeck 2938 // if a duration is supplied (and play-note event was scheduled
71     // successfully above), then schedule a subsequent stop-note event
72 schoenebeck 2879 if (id && duration > 0) {
73 schoenebeck 2938 e.Type = Event::type_stop_note;
74     e.Param.Note.ID = id;
75 schoenebeck 2871 e.Param.Note.Velocity = 127;
76     pEngineChannel->ScheduleEventMicroSec(&e, duration);
77     }
78    
79 schoenebeck 2879 // even if id is null, don't return an errorResult() here, because that
80     // would abort the script, and under heavy load it may be considerable
81     // that ScheduleNoteMicroSec() fails above, so simply ignore that
82     return successResult( ScriptID::fromNoteID(id) );
83 schoenebeck 2598 }
84    
85 schoenebeck 2931 // set_controller() function
86    
87 schoenebeck 2600 InstrumentScriptVMFunction_set_controller::InstrumentScriptVMFunction_set_controller(InstrumentScriptVM* parent)
88     : m_vm(parent)
89     {
90     }
91    
92     VMFnResult* InstrumentScriptVMFunction_set_controller::exec(VMFnArgs* args) {
93     int controller = args->arg(0)->asInt()->evalInt();
94     int value = args->arg(1)->asInt()->evalInt();
95    
96     AbstractEngineChannel* pEngineChannel =
97     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
98    
99 schoenebeck 2879 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
100     e.Init(); // clear IDs
101 schoenebeck 2600 if (controller == CTRL_TABLE_IDX_AFTERTOUCH) {
102     e.Type = Event::type_channel_pressure;
103     e.Param.ChannelPressure.Value = value & 127;
104     } else if (controller == CTRL_TABLE_IDX_PITCHBEND) {
105     e.Type = Event::type_pitchbend;
106     e.Param.Pitch.Pitch = value;
107     } else if (controller >= 0 && controller <= 127) {
108     e.Type = Event::type_control_change;
109     e.Param.CC.Controller = controller;
110     e.Param.CC.Value = value;
111     } else {
112     errMsg("set_controller(): argument 1 is an invalid controller");
113     return errorResult();
114     }
115    
116 schoenebeck 2879 const event_id_t id = pEngineChannel->ScheduleEventMicroSec(&e, 0);
117 schoenebeck 2600
118 schoenebeck 2879 // even if id is null, don't return an errorResult() here, because that
119     // would abort the script, and under heavy load it may be considerable
120     // that ScheduleEventMicroSec() fails above, so simply ignore that
121     return successResult( ScriptID::fromEventID(id) );
122 schoenebeck 2931 }
123 schoenebeck 2600
124 schoenebeck 2931 // ignore_event() function
125    
126 schoenebeck 2598 InstrumentScriptVMFunction_ignore_event::InstrumentScriptVMFunction_ignore_event(InstrumentScriptVM* parent)
127     : m_vm(parent)
128     {
129     }
130    
131 schoenebeck 2630 bool InstrumentScriptVMFunction_ignore_event::acceptsArgType(int iArg, ExprType_t type) const {
132     return type == INT_EXPR || type == INT_ARR_EXPR;
133     }
134    
135 schoenebeck 2598 VMFnResult* InstrumentScriptVMFunction_ignore_event::exec(VMFnArgs* args) {
136     AbstractEngineChannel* pEngineChannel =
137 schoenebeck 2630 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
138 schoenebeck 2598
139 schoenebeck 3212 if (args->argsCount() == 0 || args->arg(0)->exprType() == INT_EXPR) {
140     const ScriptID id = (args->argsCount() >= 1) ? args->arg(0)->asInt()->evalInt() : m_vm->m_event->id;
141     if (!id && args->argsCount() >= 1) {
142 schoenebeck 2879 wrnMsg("ignore_event(): event ID argument may not be zero");
143     // not errorResult(), because that would abort the script, not intentional in this case
144 schoenebeck 2630 return successResult();
145     }
146 schoenebeck 2879 pEngineChannel->IgnoreEventByScriptID(id);
147 schoenebeck 2630 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
148     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
149     for (int i = 0; i < ids->arraySize(); ++i) {
150 schoenebeck 2879 const ScriptID id = ids->evalIntElement(i);
151     pEngineChannel->IgnoreEventByScriptID(id);
152 schoenebeck 2630 }
153     }
154 schoenebeck 2598
155 schoenebeck 2596 return successResult();
156     }
157    
158 schoenebeck 2931 // ignore_controller() function
159    
160 schoenebeck 2598 InstrumentScriptVMFunction_ignore_controller::InstrumentScriptVMFunction_ignore_controller(InstrumentScriptVM* parent)
161     : m_vm(parent)
162     {
163     }
164    
165     VMFnResult* InstrumentScriptVMFunction_ignore_controller::exec(VMFnArgs* args) {
166 schoenebeck 2879 const ScriptID id = (args->argsCount() >= 1) ? args->arg(0)->asInt()->evalInt() : m_vm->m_event->id;
167     if (!id && args->argsCount() >= 1) {
168     wrnMsg("ignore_controller(): event ID argument may not be zero");
169 schoenebeck 2598 return successResult();
170     }
171    
172     AbstractEngineChannel* pEngineChannel =
173     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
174    
175 schoenebeck 2879 pEngineChannel->IgnoreEventByScriptID(id);
176 schoenebeck 2598
177     return successResult();
178     }
179    
180 schoenebeck 2931 // note_off() function
181    
182 schoenebeck 2629 InstrumentScriptVMFunction_note_off::InstrumentScriptVMFunction_note_off(InstrumentScriptVM* parent)
183     : m_vm(parent)
184     {
185     }
186    
187 schoenebeck 2630 bool InstrumentScriptVMFunction_note_off::acceptsArgType(int iArg, ExprType_t type) const {
188     return type == INT_EXPR || type == INT_ARR_EXPR;
189     }
190    
191 schoenebeck 2629 VMFnResult* InstrumentScriptVMFunction_note_off::exec(VMFnArgs* args) {
192 schoenebeck 2630 AbstractEngineChannel* pEngineChannel =
193     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
194    
195 schoenebeck 2629 int velocity = (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : 127;
196 schoenebeck 2630 if (velocity < 0 || velocity > 127) {
197     errMsg("note_off(): argument 2 is an invalid velocity value");
198     return errorResult();
199     }
200 schoenebeck 2629
201 schoenebeck 2630 if (args->arg(0)->exprType() == INT_EXPR) {
202 schoenebeck 2879 const ScriptID id = args->arg(0)->asInt()->evalInt();
203     if (!id) {
204     wrnMsg("note_off(): note ID for argument 1 may not be zero");
205 schoenebeck 2630 return successResult();
206     }
207 schoenebeck 2879 if (!id.isNoteID()) {
208     wrnMsg("note_off(): argument 1 is not a note ID");
209     return successResult();
210     }
211 schoenebeck 2630
212 schoenebeck 2879 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
213     if (!pNote) return successResult();
214 schoenebeck 2630
215 schoenebeck 2879 Event e = pNote->cause;
216     e.Init(); // clear IDs
217     e.CopyTimeFrom(m_vm->m_event->cause); // set fragment time for "now"
218 schoenebeck 2938 e.Type = Event::type_stop_note;
219     e.Param.Note.ID = id.noteID();
220     e.Param.Note.Key = pNote->hostKey;
221 schoenebeck 2630 e.Param.Note.Velocity = velocity;
222    
223 schoenebeck 2879 pEngineChannel->ScheduleEventMicroSec(&e, 0);
224 schoenebeck 2630 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
225     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
226     for (int i = 0; i < ids->arraySize(); ++i) {
227 schoenebeck 2879 const ScriptID id = ids->evalIntElement(i);
228     if (!id || !id.isNoteID()) continue;
229 schoenebeck 2630
230 schoenebeck 2879 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
231     if (!pNote) continue;
232 schoenebeck 2630
233 schoenebeck 2879 Event e = pNote->cause;
234     e.Init(); // clear IDs
235     e.CopyTimeFrom(m_vm->m_event->cause); // set fragment time for "now"
236 schoenebeck 2938 e.Type = Event::type_stop_note;
237     e.Param.Note.ID = id.noteID();
238     e.Param.Note.Key = pNote->hostKey;
239 schoenebeck 2630 e.Param.Note.Velocity = velocity;
240    
241 schoenebeck 2879 pEngineChannel->ScheduleEventMicroSec(&e, 0);
242 schoenebeck 2630 }
243 schoenebeck 2629 }
244    
245 schoenebeck 2630 return successResult();
246     }
247    
248 schoenebeck 2931 // set_event_mark() function
249    
250 schoenebeck 2630 InstrumentScriptVMFunction_set_event_mark::InstrumentScriptVMFunction_set_event_mark(InstrumentScriptVM* parent)
251     : m_vm(parent)
252     {
253     }
254    
255     VMFnResult* InstrumentScriptVMFunction_set_event_mark::exec(VMFnArgs* args) {
256 schoenebeck 2879 const ScriptID id = args->arg(0)->asInt()->evalInt();
257     const int groupID = args->arg(1)->asInt()->evalInt();
258 schoenebeck 2630
259     if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) {
260     errMsg("set_event_mark(): argument 2 is an invalid group id");
261 schoenebeck 2629 return errorResult();
262     }
263    
264     AbstractEngineChannel* pEngineChannel =
265     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
266    
267 schoenebeck 2879 // check if the event/note still exists
268     switch (id.type()) {
269     case ScriptID::EVENT: {
270     RTList<Event>::Iterator itEvent = pEngineChannel->pEngine->EventByID( id.eventID() );
271     if (!itEvent) return successResult();
272     break;
273     }
274     case ScriptID::NOTE: {
275     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
276     if (!pNote) return successResult();
277     break;
278     }
279     }
280 schoenebeck 2629
281 schoenebeck 2879 pEngineChannel->pScript->eventGroups[groupID].insert(id);
282 schoenebeck 2629
283 schoenebeck 2630 return successResult();
284     }
285 schoenebeck 2629
286 schoenebeck 2931 // delete_event_mark() function
287    
288 schoenebeck 2630 InstrumentScriptVMFunction_delete_event_mark::InstrumentScriptVMFunction_delete_event_mark(InstrumentScriptVM* parent)
289     : m_vm(parent)
290     {
291     }
292    
293     VMFnResult* InstrumentScriptVMFunction_delete_event_mark::exec(VMFnArgs* args) {
294 schoenebeck 2879 const ScriptID id = args->arg(0)->asInt()->evalInt();
295     const int groupID = args->arg(1)->asInt()->evalInt();
296 schoenebeck 2630
297     if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) {
298     errMsg("delete_event_mark(): argument 2 is an invalid group id");
299     return errorResult();
300     }
301    
302     AbstractEngineChannel* pEngineChannel =
303     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
304    
305 schoenebeck 2879 pEngineChannel->pScript->eventGroups[groupID].erase(id);
306 schoenebeck 2630
307 schoenebeck 2629 return successResult();
308     }
309    
310 schoenebeck 2931 // by_marks() function
311    
312 schoenebeck 2630 InstrumentScriptVMFunction_by_marks::InstrumentScriptVMFunction_by_marks(InstrumentScriptVM* parent)
313     : m_vm(parent)
314     {
315     }
316    
317     int InstrumentScriptVMFunction_by_marks::Result::arraySize() const {
318     return eventGroup->size();
319     }
320    
321     int InstrumentScriptVMFunction_by_marks::Result::evalIntElement(uint i) {
322     return (*eventGroup)[i];
323     }
324    
325     VMFnResult* InstrumentScriptVMFunction_by_marks::errorResult() {
326     m_result.eventGroup = NULL;
327     m_result.flags = StmtFlags_t(STMT_ABORT_SIGNALLED | STMT_ERROR_OCCURRED);
328     return &m_result;
329     }
330    
331     VMFnResult* InstrumentScriptVMFunction_by_marks::successResult(EventGroup* eventGroup) {
332     m_result.eventGroup = eventGroup;
333     m_result.flags = STMT_SUCCESS;
334     return &m_result;
335     }
336    
337     VMFnResult* InstrumentScriptVMFunction_by_marks::exec(VMFnArgs* args) {
338     int groupID = args->arg(0)->asInt()->evalInt();
339    
340     if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) {
341     errMsg("by_marks(): argument is an invalid group id");
342     return errorResult();
343     }
344    
345     AbstractEngineChannel* pEngineChannel =
346     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
347    
348     return successResult( &pEngineChannel->pScript->eventGroups[groupID] );
349     }
350    
351 schoenebeck 2931 // change_vol() function
352    
353     InstrumentScriptVMFunction_change_vol::InstrumentScriptVMFunction_change_vol(InstrumentScriptVM* parent)
354     : m_vm(parent)
355     {
356     }
357    
358     bool InstrumentScriptVMFunction_change_vol::acceptsArgType(int iArg, ExprType_t type) const {
359     if (iArg == 0)
360     return type == INT_EXPR || type == INT_ARR_EXPR;
361     else
362 schoenebeck 3188 return type == INT_EXPR;
363 schoenebeck 2931 }
364    
365     VMFnResult* InstrumentScriptVMFunction_change_vol::exec(VMFnArgs* args) {
366     int volume = args->arg(1)->asInt()->evalInt(); // volume change in milli dB
367     bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;
368 schoenebeck 2962 const float fVolumeLin = RTMath::DecibelToLinRatio(float(volume) / 1000.f);
369 schoenebeck 2931
370     AbstractEngineChannel* pEngineChannel =
371     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
372    
373     if (args->arg(0)->exprType() == INT_EXPR) {
374     const ScriptID id = args->arg(0)->asInt()->evalInt();
375     if (!id) {
376     wrnMsg("change_vol(): note ID for argument 1 may not be zero");
377     return successResult();
378     }
379     if (!id.isNoteID()) {
380     wrnMsg("change_vol(): argument 1 is not a note ID");
381     return successResult();
382     }
383    
384     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
385     if (!pNote) return successResult();
386    
387 schoenebeck 2962 // if change_vol() was called immediately after note was triggered
388     // then immediately apply the volume to note object
389 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
390 schoenebeck 2962 if (relative)
391 schoenebeck 2931 pNote->Override.Volume *= fVolumeLin;
392     else
393 schoenebeck 2962 pNote->Override.Volume = fVolumeLin;
394     } else { // otherwise schedule the volume change ...
395 schoenebeck 2931 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
396     e.Init(); // clear IDs
397     e.Type = Event::type_note_synth_param;
398     e.Param.NoteSynthParam.NoteID = id.noteID();
399     e.Param.NoteSynthParam.Type = Event::synth_param_volume;
400     e.Param.NoteSynthParam.Delta = fVolumeLin;
401     e.Param.NoteSynthParam.Relative = relative;
402    
403     pEngineChannel->ScheduleEventMicroSec(&e, 0);
404     }
405 schoenebeck 2962 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
406     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
407     for (int i = 0; i < ids->arraySize(); ++i) {
408     const ScriptID id = ids->evalIntElement(i);
409     if (!id || !id.isNoteID()) continue;
410    
411     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
412     if (!pNote) continue;
413    
414     // if change_vol() was called immediately after note was triggered
415     // then immediately apply the volume to Note object
416 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
417 schoenebeck 2962 if (relative)
418     pNote->Override.Volume *= fVolumeLin;
419     else
420     pNote->Override.Volume = fVolumeLin;
421     } else { // otherwise schedule the volume change ...
422     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
423     e.Init(); // clear IDs
424     e.Type = Event::type_note_synth_param;
425     e.Param.NoteSynthParam.NoteID = id.noteID();
426     e.Param.NoteSynthParam.Type = Event::synth_param_volume;
427     e.Param.NoteSynthParam.Delta = fVolumeLin;
428     e.Param.NoteSynthParam.Relative = relative;
429    
430     pEngineChannel->ScheduleEventMicroSec(&e, 0);
431     }
432     }
433 schoenebeck 2931 }
434    
435     return successResult();
436     }
437    
438     // change_tune() function
439    
440     InstrumentScriptVMFunction_change_tune::InstrumentScriptVMFunction_change_tune(InstrumentScriptVM* parent)
441     : m_vm(parent)
442     {
443     }
444    
445     bool InstrumentScriptVMFunction_change_tune::acceptsArgType(int iArg, ExprType_t type) const {
446     if (iArg == 0)
447     return type == INT_EXPR || type == INT_ARR_EXPR;
448     else
449 schoenebeck 3188 return type == INT_EXPR;
450 schoenebeck 2931 }
451    
452     VMFnResult* InstrumentScriptVMFunction_change_tune::exec(VMFnArgs* args) {
453     int tune = args->arg(1)->asInt()->evalInt(); // tuning change in milli cents
454     bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;
455 schoenebeck 2962 const float fFreqRatio = RTMath::CentsToFreqRatioUnlimited(float(tune) / 1000.f);
456 schoenebeck 2931
457     AbstractEngineChannel* pEngineChannel =
458     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
459    
460     if (args->arg(0)->exprType() == INT_EXPR) {
461     const ScriptID id = args->arg(0)->asInt()->evalInt();
462     if (!id) {
463     wrnMsg("change_tune(): note ID for argument 1 may not be zero");
464     return successResult();
465     }
466     if (!id.isNoteID()) {
467     wrnMsg("change_tune(): argument 1 is not a note ID");
468     return successResult();
469     }
470    
471     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
472     if (!pNote) return successResult();
473    
474 schoenebeck 2962 // if change_tune() was called immediately after note was triggered
475     // then immediately apply the tuning to Note object
476 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
477 schoenebeck 2962 if (relative)
478 schoenebeck 2931 pNote->Override.Pitch *= fFreqRatio;
479     else
480 schoenebeck 2962 pNote->Override.Pitch = fFreqRatio;
481     } else { // otherwise schedule tuning change ...
482 schoenebeck 2931 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
483     e.Init(); // clear IDs
484     e.Type = Event::type_note_synth_param;
485     e.Param.NoteSynthParam.NoteID = id.noteID();
486     e.Param.NoteSynthParam.Type = Event::synth_param_pitch;
487     e.Param.NoteSynthParam.Delta = fFreqRatio;
488     e.Param.NoteSynthParam.Relative = relative;
489    
490     pEngineChannel->ScheduleEventMicroSec(&e, 0);
491     }
492 schoenebeck 2962 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
493     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
494     for (int i = 0; i < ids->arraySize(); ++i) {
495     const ScriptID id = ids->evalIntElement(i);
496     if (!id || !id.isNoteID()) continue;
497    
498     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
499     if (!pNote) continue;
500    
501     // if change_tune() was called immediately after note was triggered
502     // then immediately apply the tuning to Note object
503 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
504 schoenebeck 2962 if (relative)
505     pNote->Override.Pitch *= fFreqRatio;
506     else
507     pNote->Override.Pitch = fFreqRatio;
508     } else { // otherwise schedule tuning change ...
509     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
510     e.Init(); // clear IDs
511     e.Type = Event::type_note_synth_param;
512     e.Param.NoteSynthParam.NoteID = id.noteID();
513     e.Param.NoteSynthParam.Type = Event::synth_param_pitch;
514     e.Param.NoteSynthParam.Delta = fFreqRatio;
515     e.Param.NoteSynthParam.Relative = relative;
516    
517     pEngineChannel->ScheduleEventMicroSec(&e, 0);
518     }
519     }
520 schoenebeck 2931 }
521    
522     return successResult();
523     }
524    
525     // change_pan() function
526    
527     InstrumentScriptVMFunction_change_pan::InstrumentScriptVMFunction_change_pan(InstrumentScriptVM* parent)
528     : m_vm(parent)
529     {
530     }
531    
532     bool InstrumentScriptVMFunction_change_pan::acceptsArgType(int iArg, ExprType_t type) const {
533     if (iArg == 0)
534     return type == INT_EXPR || type == INT_ARR_EXPR;
535     else
536 schoenebeck 3188 return type == INT_EXPR;
537 schoenebeck 2931 }
538    
539     VMFnResult* InstrumentScriptVMFunction_change_pan::exec(VMFnArgs* args) {
540     int pan = args->arg(1)->asInt()->evalInt();
541     bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;
542    
543     if (pan > 1000) {
544     wrnMsg("change_pan(): argument 2 may not be larger than 1000");
545     pan = 1000;
546     } else if (pan < -1000) {
547     wrnMsg("change_pan(): argument 2 may not be smaller than -1000");
548     pan = -1000;
549     }
550 schoenebeck 2962 const float fPan = float(pan) / 1000.f;
551 schoenebeck 2931
552     AbstractEngineChannel* pEngineChannel =
553     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
554    
555     if (args->arg(0)->exprType() == INT_EXPR) {
556     const ScriptID id = args->arg(0)->asInt()->evalInt();
557     if (!id) {
558     wrnMsg("change_pan(): note ID for argument 1 may not be zero");
559     return successResult();
560     }
561     if (!id.isNoteID()) {
562     wrnMsg("change_pan(): argument 1 is not a note ID");
563     return successResult();
564     }
565    
566     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
567     if (!pNote) return successResult();
568    
569 schoenebeck 2962 // if change_pan() was called immediately after note was triggered
570     // then immediately apply the panning to Note object
571 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
572 schoenebeck 2962 if (relative) {
573 schoenebeck 2931 pNote->Override.Pan = RTMath::RelativeSummedAvg(pNote->Override.Pan, fPan, ++pNote->Override.PanSources);
574     } else {
575     pNote->Override.Pan = fPan;
576     pNote->Override.PanSources = 1; // only relevant on subsequent change_pan() calls on same note with 'relative' being set
577 schoenebeck 2962 }
578     } else { // otherwise schedule panning change ...
579 schoenebeck 2931 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
580     e.Init(); // clear IDs
581     e.Type = Event::type_note_synth_param;
582     e.Param.NoteSynthParam.NoteID = id.noteID();
583     e.Param.NoteSynthParam.Type = Event::synth_param_pan;
584     e.Param.NoteSynthParam.Delta = fPan;
585     e.Param.NoteSynthParam.Relative = relative;
586    
587     pEngineChannel->ScheduleEventMicroSec(&e, 0);
588     }
589 schoenebeck 2962 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
590     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
591     for (int i = 0; i < ids->arraySize(); ++i) {
592     const ScriptID id = ids->evalIntElement(i);
593     if (!id || !id.isNoteID()) continue;
594    
595     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
596     if (!pNote) continue;
597    
598     // if change_pan() was called immediately after note was triggered
599     // then immediately apply the panning to Note object
600 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
601 schoenebeck 2962 if (relative) {
602     pNote->Override.Pan = RTMath::RelativeSummedAvg(pNote->Override.Pan, fPan, ++pNote->Override.PanSources);
603     } else {
604     pNote->Override.Pan = fPan;
605     pNote->Override.PanSources = 1; // only relevant on subsequent change_pan() calls on same note with 'relative' being set
606     }
607     } else { // otherwise schedule panning change ...
608     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
609     e.Init(); // clear IDs
610     e.Type = Event::type_note_synth_param;
611     e.Param.NoteSynthParam.NoteID = id.noteID();
612     e.Param.NoteSynthParam.Type = Event::synth_param_pan;
613     e.Param.NoteSynthParam.Delta = fPan;
614     e.Param.NoteSynthParam.Relative = relative;
615    
616     pEngineChannel->ScheduleEventMicroSec(&e, 0);
617     }
618     }
619 schoenebeck 2931 }
620    
621     return successResult();
622     }
623    
624 schoenebeck 2935 #define VM_FILTER_PAR_MAX_VALUE 1000000
625 schoenebeck 2953 #define VM_EG_PAR_MAX_VALUE 1000000
626 schoenebeck 2935
627     // change_cutoff() function
628    
629     InstrumentScriptVMFunction_change_cutoff::InstrumentScriptVMFunction_change_cutoff(InstrumentScriptVM* parent)
630     : m_vm(parent)
631     {
632     }
633    
634     bool InstrumentScriptVMFunction_change_cutoff::acceptsArgType(int iArg, ExprType_t type) const {
635     if (iArg == 0)
636     return type == INT_EXPR || type == INT_ARR_EXPR;
637     else
638 schoenebeck 3188 return type == INT_EXPR;
639 schoenebeck 2935 }
640    
641     VMFnResult* InstrumentScriptVMFunction_change_cutoff::exec(VMFnArgs* args) {
642     int cutoff = args->arg(1)->asInt()->evalInt();
643     if (cutoff > VM_FILTER_PAR_MAX_VALUE) {
644     wrnMsg("change_cutoff(): argument 2 may not be larger than 1000000");
645     cutoff = VM_FILTER_PAR_MAX_VALUE;
646     } else if (cutoff < 0) {
647     wrnMsg("change_cutoff(): argument 2 may not be negative");
648     cutoff = 0;
649     }
650 schoenebeck 2962 const float fCutoff = float(cutoff) / float(VM_FILTER_PAR_MAX_VALUE);
651 schoenebeck 2935
652     AbstractEngineChannel* pEngineChannel =
653     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
654    
655     if (args->arg(0)->exprType() == INT_EXPR) {
656     const ScriptID id = args->arg(0)->asInt()->evalInt();
657     if (!id) {
658     wrnMsg("change_cutoff(): note ID for argument 1 may not be zero");
659     return successResult();
660     }
661     if (!id.isNoteID()) {
662     wrnMsg("change_cutoff(): argument 1 is not a note ID");
663     return successResult();
664     }
665    
666     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
667     if (!pNote) return successResult();
668    
669 schoenebeck 2962 // if change_cutoff() was called immediately after note was triggered
670     // then immediately apply cutoff to Note object
671 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
672 schoenebeck 2962 pNote->Override.Cutoff = fCutoff;
673     } else { // otherwise schedule cutoff change ...
674     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
675     e.Init(); // clear IDs
676     e.Type = Event::type_note_synth_param;
677     e.Param.NoteSynthParam.NoteID = id.noteID();
678     e.Param.NoteSynthParam.Type = Event::synth_param_cutoff;
679     e.Param.NoteSynthParam.Delta = fCutoff;
680     e.Param.NoteSynthParam.Relative = false;
681 schoenebeck 2935
682 schoenebeck 2962 pEngineChannel->ScheduleEventMicroSec(&e, 0);
683     }
684 schoenebeck 2935 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
685     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
686     for (int i = 0; i < ids->arraySize(); ++i) {
687     const ScriptID id = ids->evalIntElement(i);
688     if (!id || !id.isNoteID()) continue;
689    
690     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
691     if (!pNote) continue;
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 }
709     }
710    
711     return successResult();
712     }
713    
714     // change_reso() function
715    
716     InstrumentScriptVMFunction_change_reso::InstrumentScriptVMFunction_change_reso(InstrumentScriptVM* parent)
717     : m_vm(parent)
718     {
719     }
720    
721     bool InstrumentScriptVMFunction_change_reso::acceptsArgType(int iArg, ExprType_t type) const {
722     if (iArg == 0)
723     return type == INT_EXPR || type == INT_ARR_EXPR;
724     else
725 schoenebeck 3188 return type == INT_EXPR;
726 schoenebeck 2935 }
727    
728     VMFnResult* InstrumentScriptVMFunction_change_reso::exec(VMFnArgs* args) {
729     int resonance = args->arg(1)->asInt()->evalInt();
730     if (resonance > VM_FILTER_PAR_MAX_VALUE) {
731     wrnMsg("change_reso(): argument 2 may not be larger than 1000000");
732     resonance = VM_FILTER_PAR_MAX_VALUE;
733     } else if (resonance < 0) {
734     wrnMsg("change_reso(): argument 2 may not be negative");
735     resonance = 0;
736     }
737 schoenebeck 2962 const float fResonance = float(resonance) / float(VM_FILTER_PAR_MAX_VALUE);
738 schoenebeck 2935
739     AbstractEngineChannel* pEngineChannel =
740     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
741    
742     if (args->arg(0)->exprType() == INT_EXPR) {
743     const ScriptID id = args->arg(0)->asInt()->evalInt();
744     if (!id) {
745     wrnMsg("change_reso(): note ID for argument 1 may not be zero");
746     return successResult();
747     }
748     if (!id.isNoteID()) {
749     wrnMsg("change_reso(): argument 1 is not a note ID");
750     return successResult();
751     }
752    
753     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
754     if (!pNote) return successResult();
755    
756 schoenebeck 2962 // if change_reso() was called immediately after note was triggered
757     // then immediately apply resonance to Note object
758 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
759 schoenebeck 2962 pNote->Override.Resonance = fResonance;
760     } else { // otherwise schedule resonance change ...
761     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
762     e.Init(); // clear IDs
763     e.Type = Event::type_note_synth_param;
764     e.Param.NoteSynthParam.NoteID = id.noteID();
765     e.Param.NoteSynthParam.Type = Event::synth_param_resonance;
766     e.Param.NoteSynthParam.Delta = fResonance;
767     e.Param.NoteSynthParam.Relative = false;
768 schoenebeck 2935
769 schoenebeck 2962 pEngineChannel->ScheduleEventMicroSec(&e, 0);
770     }
771 schoenebeck 2935 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
772     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
773     for (int i = 0; i < ids->arraySize(); ++i) {
774     const ScriptID id = ids->evalIntElement(i);
775     if (!id || !id.isNoteID()) continue;
776    
777     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
778     if (!pNote) continue;
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 }
796     }
797    
798     return successResult();
799     }
800 schoenebeck 2953
801     // change_attack() function
802 schoenebeck 2935
803 schoenebeck 2953 InstrumentScriptVMFunction_change_attack::InstrumentScriptVMFunction_change_attack(InstrumentScriptVM* parent)
804     : m_vm(parent)
805     {
806     }
807    
808     bool InstrumentScriptVMFunction_change_attack::acceptsArgType(int iArg, ExprType_t type) const {
809     if (iArg == 0)
810     return type == INT_EXPR || type == INT_ARR_EXPR;
811     else
812 schoenebeck 3188 return type == INT_EXPR;
813 schoenebeck 2953 }
814    
815     VMFnResult* InstrumentScriptVMFunction_change_attack::exec(VMFnArgs* args) {
816     int attack = args->arg(1)->asInt()->evalInt();
817     if (attack > VM_EG_PAR_MAX_VALUE) {
818     wrnMsg("change_attack(): argument 2 may not be larger than 1000000");
819     attack = VM_EG_PAR_MAX_VALUE;
820     } else if (attack < 0) {
821     wrnMsg("change_attack(): argument 2 may not be negative");
822     attack = 0;
823     }
824     const float fAttack = float(attack) / float(VM_EG_PAR_MAX_VALUE);
825    
826     AbstractEngineChannel* pEngineChannel =
827     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
828    
829     if (args->arg(0)->exprType() == INT_EXPR) {
830     const ScriptID id = args->arg(0)->asInt()->evalInt();
831     if (!id) {
832     wrnMsg("change_attack(): note ID for argument 1 may not be zero");
833     return successResult();
834     }
835     if (!id.isNoteID()) {
836     wrnMsg("change_attack(): argument 1 is not a note ID");
837     return successResult();
838     }
839    
840     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
841 schoenebeck 2962 if (!pNote) return successResult();
842 schoenebeck 2953
843 schoenebeck 2962 // if change_attack() was called immediately after note was triggered
844     // then immediately apply attack to Note object
845 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
846 schoenebeck 2962 pNote->Override.Attack = fAttack;
847     } else { // otherwise schedule attack change ...
848     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
849     e.Init(); // clear IDs
850     e.Type = Event::type_note_synth_param;
851     e.Param.NoteSynthParam.NoteID = id.noteID();
852     e.Param.NoteSynthParam.Type = Event::synth_param_attack;
853     e.Param.NoteSynthParam.Delta = fAttack;
854     e.Param.NoteSynthParam.Relative = false;
855 schoenebeck 2953
856 schoenebeck 2962 pEngineChannel->ScheduleEventMicroSec(&e, 0);
857     }
858 schoenebeck 2953 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
859     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
860     for (int i = 0; i < ids->arraySize(); ++i) {
861     const ScriptID id = ids->evalIntElement(i);
862     if (!id || !id.isNoteID()) continue;
863    
864     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
865     if (!pNote) continue;
866    
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 }
883     }
884    
885     return successResult();
886     }
887    
888     // change_decay() function
889    
890     InstrumentScriptVMFunction_change_decay::InstrumentScriptVMFunction_change_decay(InstrumentScriptVM* parent)
891     : m_vm(parent)
892     {
893     }
894    
895     bool InstrumentScriptVMFunction_change_decay::acceptsArgType(int iArg, ExprType_t type) const {
896     if (iArg == 0)
897     return type == INT_EXPR || type == INT_ARR_EXPR;
898     else
899 schoenebeck 3188 return type == INT_EXPR;
900 schoenebeck 2953 }
901    
902     VMFnResult* InstrumentScriptVMFunction_change_decay::exec(VMFnArgs* args) {
903     int decay = args->arg(1)->asInt()->evalInt();
904     if (decay > VM_EG_PAR_MAX_VALUE) {
905     wrnMsg("change_decay(): argument 2 may not be larger than 1000000");
906     decay = VM_EG_PAR_MAX_VALUE;
907     } else if (decay < 0) {
908     wrnMsg("change_decay(): argument 2 may not be negative");
909     decay = 0;
910     }
911     const float fDecay = float(decay) / float(VM_EG_PAR_MAX_VALUE);
912    
913     AbstractEngineChannel* pEngineChannel =
914     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
915    
916     if (args->arg(0)->exprType() == INT_EXPR) {
917     const ScriptID id = args->arg(0)->asInt()->evalInt();
918     if (!id) {
919     wrnMsg("change_decay(): note ID for argument 1 may not be zero");
920     return successResult();
921     }
922     if (!id.isNoteID()) {
923     wrnMsg("change_decay(): argument 1 is not a note ID");
924     return successResult();
925     }
926    
927     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
928 schoenebeck 2962 if (!pNote) return successResult();
929 schoenebeck 2953
930 schoenebeck 2962 // if change_decay() was called immediately after note was triggered
931     // then immediately apply decay to Note object
932 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
933 schoenebeck 2962 pNote->Override.Decay = fDecay;
934     } else { // otherwise schedule decay change ...
935     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
936     e.Init(); // clear IDs
937     e.Type = Event::type_note_synth_param;
938     e.Param.NoteSynthParam.NoteID = id.noteID();
939     e.Param.NoteSynthParam.Type = Event::synth_param_decay;
940     e.Param.NoteSynthParam.Delta = fDecay;
941     e.Param.NoteSynthParam.Relative = false;
942 schoenebeck 2953
943 schoenebeck 2962 pEngineChannel->ScheduleEventMicroSec(&e, 0);
944     }
945 schoenebeck 2953 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
946     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
947     for (int i = 0; i < ids->arraySize(); ++i) {
948     const ScriptID id = ids->evalIntElement(i);
949     if (!id || !id.isNoteID()) continue;
950    
951     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
952     if (!pNote) continue;
953    
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 }
970     }
971    
972     return successResult();
973     }
974    
975     // change_release() function
976    
977     InstrumentScriptVMFunction_change_release::InstrumentScriptVMFunction_change_release(InstrumentScriptVM* parent)
978     : m_vm(parent)
979     {
980     }
981    
982     bool InstrumentScriptVMFunction_change_release::acceptsArgType(int iArg, ExprType_t type) const {
983     if (iArg == 0)
984     return type == INT_EXPR || type == INT_ARR_EXPR;
985     else
986 schoenebeck 3188 return type == INT_EXPR;
987 schoenebeck 2953 }
988    
989     VMFnResult* InstrumentScriptVMFunction_change_release::exec(VMFnArgs* args) {
990     int release = args->arg(1)->asInt()->evalInt();
991     if (release > VM_EG_PAR_MAX_VALUE) {
992     wrnMsg("change_release(): argument 2 may not be larger than 1000000");
993     release = VM_EG_PAR_MAX_VALUE;
994     } else if (release < 0) {
995     wrnMsg("change_release(): argument 2 may not be negative");
996     release = 0;
997     }
998     const float fRelease = float(release) / float(VM_EG_PAR_MAX_VALUE);
999    
1000     AbstractEngineChannel* pEngineChannel =
1001     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1002    
1003     if (args->arg(0)->exprType() == INT_EXPR) {
1004     const ScriptID id = args->arg(0)->asInt()->evalInt();
1005     if (!id) {
1006     wrnMsg("change_release(): note ID for argument 1 may not be zero");
1007     return successResult();
1008     }
1009     if (!id.isNoteID()) {
1010     wrnMsg("change_release(): argument 1 is not a note ID");
1011     return successResult();
1012     }
1013    
1014     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1015 schoenebeck 2962 if (!pNote) return successResult();
1016 schoenebeck 2953
1017 schoenebeck 2962 // if change_release() was called immediately after note was triggered
1018     // then immediately apply relase to Note object
1019 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1020 schoenebeck 2962 pNote->Override.Release = fRelease;
1021     } else { // otherwise schedule release change ...
1022     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1023     e.Init(); // clear IDs
1024     e.Type = Event::type_note_synth_param;
1025     e.Param.NoteSynthParam.NoteID = id.noteID();
1026     e.Param.NoteSynthParam.Type = Event::synth_param_release;
1027     e.Param.NoteSynthParam.Delta = fRelease;
1028     e.Param.NoteSynthParam.Relative = false;
1029 schoenebeck 2953
1030 schoenebeck 2962 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1031     }
1032 schoenebeck 2953 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1033     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1034     for (int i = 0; i < ids->arraySize(); ++i) {
1035     const ScriptID id = ids->evalIntElement(i);
1036     if (!id || !id.isNoteID()) continue;
1037    
1038     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1039     if (!pNote) continue;
1040    
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 }
1057     }
1058    
1059     return successResult();
1060     }
1061    
1062 schoenebeck 3118 bool VMChangeSynthParamFunction::acceptsArgType(int iArg, ExprType_t type) const {
1063     if (iArg == 0)
1064     return type == INT_EXPR || type == INT_ARR_EXPR;
1065     else
1066 schoenebeck 3188 return type == INT_EXPR;
1067 schoenebeck 3118 }
1068    
1069 schoenebeck 3193 // Arbitrarily chosen constant value symbolizing "no limit".
1070     #define NO_LIMIT 1315916909
1071    
1072 schoenebeck 3188 template<float NoteBase::_Override::*T_noteParam, int T_synthParam,
1073     bool T_isNormalizedParam, int T_maxValue, int T_minValue>
1074 schoenebeck 3118 VMFnResult* VMChangeSynthParamFunction::execTemplate(VMFnArgs* args, const char* functionName) {
1075     int value = args->arg(1)->asInt()->evalInt();
1076 schoenebeck 3193 if (T_maxValue != NO_LIMIT && value > T_maxValue) {
1077 schoenebeck 3188 wrnMsg(String(functionName) + "(): argument 2 may not be larger than " + ToString(T_maxValue));
1078     value = T_maxValue;
1079 schoenebeck 3193 } else if (T_minValue != NO_LIMIT && value < T_minValue) {
1080 schoenebeck 3188 if (T_minValue == 0)
1081     wrnMsg(String(functionName) + "(): argument 2 may not be negative");
1082     else
1083     wrnMsg(String(functionName) + "(): argument 2 may not be smaller than " + ToString(T_minValue));
1084     value = T_minValue;
1085 schoenebeck 3118 }
1086 schoenebeck 3188 const float fValue = (T_isNormalizedParam) ?
1087     float(value) / float(T_maxValue) : // convert to 0.0 .. 1.0 value range
1088     float(value) / 1000000.f; // assuming microseconds here, convert to seconds
1089 schoenebeck 3118
1090     AbstractEngineChannel* pEngineChannel =
1091     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1092    
1093     if (args->arg(0)->exprType() == INT_EXPR) {
1094     const ScriptID id = args->arg(0)->asInt()->evalInt();
1095     if (!id) {
1096     wrnMsg(String(functionName) + "(): note ID for argument 1 may not be zero");
1097     return successResult();
1098     }
1099     if (!id.isNoteID()) {
1100     wrnMsg(String(functionName) + "(): argument 1 is not a note ID");
1101     return successResult();
1102     }
1103    
1104     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1105     if (!pNote) return successResult();
1106    
1107     // if this change_*() script function was called immediately after
1108     // note was triggered then immediately apply the synth parameter
1109     // change to Note object
1110 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1111 schoenebeck 3118 pNote->Override.*T_noteParam = fValue;
1112     } else { // otherwise schedule this synth parameter change ...
1113     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1114     e.Init(); // clear IDs
1115     e.Type = Event::type_note_synth_param;
1116     e.Param.NoteSynthParam.NoteID = id.noteID();
1117     e.Param.NoteSynthParam.Type = (Event::synth_param_t) T_synthParam;
1118     e.Param.NoteSynthParam.Delta = fValue;
1119     e.Param.NoteSynthParam.Relative = false;
1120    
1121     pEngineChannel->ScheduleEventMicroSec(&e, 0);
1122     }
1123     } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1124     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1125     for (int i = 0; i < ids->arraySize(); ++i) {
1126     const ScriptID id = ids->evalIntElement(i);
1127     if (!id || !id.isNoteID()) continue;
1128    
1129     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1130     if (!pNote) continue;
1131    
1132     // if this change_*() script function was called immediately after
1133     // note was triggered then immediately apply the synth parameter
1134     // change to Note object
1135 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1136 schoenebeck 3118 pNote->Override.*T_noteParam = fValue;
1137     } else { // otherwise schedule this synth parameter change ...
1138     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1139     e.Init(); // clear IDs
1140     e.Type = Event::type_note_synth_param;
1141     e.Param.NoteSynthParam.NoteID = id.noteID();
1142     e.Param.NoteSynthParam.Type = (Event::synth_param_t) T_synthParam;
1143     e.Param.NoteSynthParam.Delta = fValue;
1144     e.Param.NoteSynthParam.Relative = false;
1145    
1146     pEngineChannel->ScheduleEventMicroSec(&e, 0);
1147     }
1148     }
1149     }
1150    
1151     return successResult();
1152     }
1153    
1154     // change_amp_lfo_depth() function
1155    
1156     VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_depth::exec(VMFnArgs* args) {
1157 schoenebeck 3188 return VMChangeSynthParamFunction::execTemplate<
1158     &NoteBase::_Override::AmpLFODepth,
1159 schoenebeck 3193 Event::synth_param_amp_lfo_depth,
1160     true, 1000000, 0>( args, "change_amp_lfo_depth" );
1161 schoenebeck 3118 }
1162    
1163     // change_amp_lfo_freq() function
1164    
1165     VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_freq::exec(VMFnArgs* args) {
1166 schoenebeck 3188 return VMChangeSynthParamFunction::execTemplate<
1167     &NoteBase::_Override::AmpLFOFreq,
1168 schoenebeck 3193 Event::synth_param_amp_lfo_freq,
1169     true, 1000000, 0>( args, "change_amp_lfo_freq" );
1170 schoenebeck 3118 }
1171    
1172     // change_pitch_lfo_depth() function
1173    
1174     VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_depth::exec(VMFnArgs* args) {
1175 schoenebeck 3188 return VMChangeSynthParamFunction::execTemplate<
1176     &NoteBase::_Override::PitchLFODepth,
1177 schoenebeck 3193 Event::synth_param_pitch_lfo_depth,
1178     true, 1000000, 0>( args, "change_pitch_lfo_depth" );
1179 schoenebeck 3118 }
1180    
1181     // change_pitch_lfo_freq() function
1182    
1183     VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_freq::exec(VMFnArgs* args) {
1184 schoenebeck 3188 return VMChangeSynthParamFunction::execTemplate<
1185     &NoteBase::_Override::PitchLFOFreq,
1186 schoenebeck 3193 Event::synth_param_pitch_lfo_freq,
1187     true, 1000000, 0>( args, "change_pitch_lfo_freq" );
1188 schoenebeck 3118 }
1189    
1190 schoenebeck 3188 // change_vol_time() function
1191    
1192     VMFnResult* InstrumentScriptVMFunction_change_vol_time::exec(VMFnArgs* args) {
1193     return VMChangeSynthParamFunction::execTemplate<
1194     &NoteBase::_Override::VolumeTime,
1195 schoenebeck 3193 Event::synth_param_volume_time,
1196     false, NO_LIMIT, 0>( args, "change_vol_time" );
1197 schoenebeck 3188 }
1198    
1199     // change_tune_time() function
1200    
1201     VMFnResult* InstrumentScriptVMFunction_change_tune_time::exec(VMFnArgs* args) {
1202     return VMChangeSynthParamFunction::execTemplate<
1203     &NoteBase::_Override::PitchTime,
1204 schoenebeck 3193 Event::synth_param_pitch_time,
1205     false, NO_LIMIT, 0>( args, "change_tune_time" );
1206 schoenebeck 3188 }
1207    
1208     // fade_in() function
1209    
1210     InstrumentScriptVMFunction_fade_in::InstrumentScriptVMFunction_fade_in(InstrumentScriptVM* parent)
1211     : m_vm(parent)
1212     {
1213     }
1214    
1215     bool InstrumentScriptVMFunction_fade_in::acceptsArgType(int iArg, ExprType_t type) const {
1216     if (iArg == 0)
1217     return type == INT_EXPR || type == INT_ARR_EXPR;
1218     else
1219     return type == INT_EXPR;
1220     }
1221    
1222     VMFnResult* InstrumentScriptVMFunction_fade_in::exec(VMFnArgs* args) {
1223     int duration = args->arg(1)->asInt()->evalInt();
1224     if (duration < 0) {
1225     wrnMsg("fade_in(): argument 2 may not be negative");
1226     duration = 0;
1227     }
1228     const float fDuration = float(duration) / 1000000.f; // convert microseconds to seconds
1229    
1230     AbstractEngineChannel* pEngineChannel =
1231     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1232    
1233     if (args->arg(0)->exprType() == INT_EXPR) {
1234     const ScriptID id = args->arg(0)->asInt()->evalInt();
1235     if (!id) {
1236     wrnMsg("fade_in(): note ID for argument 1 may not be zero");
1237     return successResult();
1238     }
1239     if (!id.isNoteID()) {
1240     wrnMsg("fade_in(): argument 1 is not a note ID");
1241     return successResult();
1242     }
1243    
1244     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1245     if (!pNote) return successResult();
1246    
1247     // if fade_in() was called immediately after note was triggered
1248     // then immediately apply a start volume of zero to Note object,
1249     // as well as the fade in duration
1250 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1251 schoenebeck 3188 pNote->Override.Volume = 0.f;
1252     pNote->Override.VolumeTime = fDuration;
1253     } else { // otherwise schedule a "volume time" change with the requested fade in duration ...
1254     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1255     e.Init(); // clear IDs
1256     e.Type = Event::type_note_synth_param;
1257     e.Param.NoteSynthParam.NoteID = id.noteID();
1258     e.Param.NoteSynthParam.Type = Event::synth_param_volume_time;
1259     e.Param.NoteSynthParam.Delta = fDuration;
1260     e.Param.NoteSynthParam.Relative = false;
1261    
1262     pEngineChannel->ScheduleEventMicroSec(&e, 0);
1263     }
1264     // and finally schedule a "volume" change, simply one time slice
1265     // ahead, with the final fade in volume (1.0)
1266     {
1267     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1268     e.Init(); // clear IDs
1269     e.Type = Event::type_note_synth_param;
1270     e.Param.NoteSynthParam.NoteID = id.noteID();
1271     e.Param.NoteSynthParam.Type = Event::synth_param_volume;
1272     e.Param.NoteSynthParam.Delta = 1.f;
1273     e.Param.NoteSynthParam.Relative = false;
1274    
1275     // scheduling with 0 delay would also work here, but +1 is more
1276     // safe regarding potential future implementation changes of the
1277     // scheduler (see API comments of RTAVLTree::insert())
1278     pEngineChannel->ScheduleEventMicroSec(&e, 1);
1279     }
1280     } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1281     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1282     for (int i = 0; i < ids->arraySize(); ++i) {
1283     const ScriptID id = ids->evalIntElement(i);
1284     if (!id || !id.isNoteID()) continue;
1285    
1286     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1287     if (!pNote) continue;
1288    
1289     // if fade_in() was called immediately after note was triggered
1290     // then immediately apply a start volume of zero to Note object,
1291     // as well as the fade in duration
1292 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1293 schoenebeck 3188 pNote->Override.Volume = 0.f;
1294     pNote->Override.VolumeTime = fDuration;
1295     } else { // otherwise schedule a "volume time" change with the requested fade in duration ...
1296     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1297     e.Init(); // clear IDs
1298     e.Type = Event::type_note_synth_param;
1299     e.Param.NoteSynthParam.NoteID = id.noteID();
1300     e.Param.NoteSynthParam.Type = Event::synth_param_volume_time;
1301     e.Param.NoteSynthParam.Delta = fDuration;
1302     e.Param.NoteSynthParam.Relative = false;
1303    
1304     pEngineChannel->ScheduleEventMicroSec(&e, 0);
1305     }
1306     // and finally schedule a "volume" change, simply one time slice
1307     // ahead, with the final fade in volume (1.0)
1308     {
1309     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1310     e.Init(); // clear IDs
1311     e.Type = Event::type_note_synth_param;
1312     e.Param.NoteSynthParam.NoteID = id.noteID();
1313     e.Param.NoteSynthParam.Type = Event::synth_param_volume;
1314     e.Param.NoteSynthParam.Delta = 1.f;
1315     e.Param.NoteSynthParam.Relative = false;
1316    
1317     // scheduling with 0 delay would also work here, but +1 is more
1318     // safe regarding potential future implementation changes of the
1319     // scheduler (see API comments of RTAVLTree::insert())
1320     pEngineChannel->ScheduleEventMicroSec(&e, 1);
1321     }
1322     }
1323     }
1324    
1325     return successResult();
1326     }
1327    
1328     // fade_out() function
1329    
1330     InstrumentScriptVMFunction_fade_out::InstrumentScriptVMFunction_fade_out(InstrumentScriptVM* parent)
1331     : m_vm(parent)
1332     {
1333     }
1334    
1335     bool InstrumentScriptVMFunction_fade_out::acceptsArgType(int iArg, ExprType_t type) const {
1336     if (iArg == 0)
1337     return type == INT_EXPR || type == INT_ARR_EXPR;
1338     else
1339     return type == INT_EXPR;
1340     }
1341    
1342     VMFnResult* InstrumentScriptVMFunction_fade_out::exec(VMFnArgs* args) {
1343     int duration = args->arg(1)->asInt()->evalInt();
1344     if (duration < 0) {
1345     wrnMsg("fade_out(): argument 2 may not be negative");
1346     duration = 0;
1347     }
1348     const float fDuration = float(duration) / 1000000.f; // convert microseconds to seconds
1349    
1350     bool stop = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : true;
1351    
1352     AbstractEngineChannel* pEngineChannel =
1353     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1354    
1355     if (args->arg(0)->exprType() == INT_EXPR) {
1356     const ScriptID id = args->arg(0)->asInt()->evalInt();
1357     if (!id) {
1358     wrnMsg("fade_out(): note ID for argument 1 may not be zero");
1359     return successResult();
1360     }
1361     if (!id.isNoteID()) {
1362     wrnMsg("fade_out(): argument 1 is not a note ID");
1363     return successResult();
1364     }
1365    
1366     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1367     if (!pNote) return successResult();
1368    
1369     // if fade_out() was called immediately after note was triggered
1370     // then immediately apply fade out duration to Note object
1371 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1372 schoenebeck 3188 pNote->Override.VolumeTime = fDuration;
1373     } else { // otherwise schedule a "volume time" change with the requested fade out duration ...
1374     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1375     e.Init(); // clear IDs
1376     e.Type = Event::type_note_synth_param;
1377     e.Param.NoteSynthParam.NoteID = id.noteID();
1378     e.Param.NoteSynthParam.Type = Event::synth_param_volume_time;
1379     e.Param.NoteSynthParam.Delta = fDuration;
1380     e.Param.NoteSynthParam.Relative = false;
1381    
1382     pEngineChannel->ScheduleEventMicroSec(&e, 0);
1383     }
1384     // now schedule a "volume" change, simply one time slice ahead, with
1385     // the final fade out volume (0.0)
1386     {
1387     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1388     e.Init(); // clear IDs
1389     e.Type = Event::type_note_synth_param;
1390     e.Param.NoteSynthParam.NoteID = id.noteID();
1391     e.Param.NoteSynthParam.Type = Event::synth_param_volume;
1392     e.Param.NoteSynthParam.Delta = 0.f;
1393     e.Param.NoteSynthParam.Relative = false;
1394    
1395     // scheduling with 0 delay would also work here, but +1 is more
1396     // safe regarding potential future implementation changes of the
1397     // scheduler (see API comments of RTAVLTree::insert())
1398     pEngineChannel->ScheduleEventMicroSec(&e, 1);
1399     }
1400     // and finally if stopping the note was requested after the fade out
1401     // completed, then schedule to kill the voice after the requested
1402     // duration
1403     if (stop) {
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_kill_note;
1407     e.Param.Note.ID = id.noteID();
1408     e.Param.Note.Key = pNote->hostKey;
1409    
1410     pEngineChannel->ScheduleEventMicroSec(&e, duration + 1);
1411     }
1412     } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1413     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1414     for (int i = 0; i < ids->arraySize(); ++i) {
1415     const ScriptID id = ids->evalIntElement(i);
1416     if (!id || !id.isNoteID()) continue;
1417    
1418     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1419     if (!pNote) continue;
1420    
1421     // if fade_out() was called immediately after note was triggered
1422     // then immediately apply fade out duration to Note object
1423 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1424 schoenebeck 3188 pNote->Override.VolumeTime = fDuration;
1425     } else { // otherwise schedule a "volume time" change with the requested fade out duration ...
1426     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1427     e.Init(); // clear IDs
1428     e.Type = Event::type_note_synth_param;
1429     e.Param.NoteSynthParam.NoteID = id.noteID();
1430     e.Param.NoteSynthParam.Type = Event::synth_param_volume_time;
1431     e.Param.NoteSynthParam.Delta = fDuration;
1432     e.Param.NoteSynthParam.Relative = false;
1433    
1434     pEngineChannel->ScheduleEventMicroSec(&e, 0);
1435     }
1436     // now schedule a "volume" change, simply one time slice ahead, with
1437     // the final fade out volume (0.0)
1438     {
1439     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1440     e.Init(); // clear IDs
1441     e.Type = Event::type_note_synth_param;
1442     e.Param.NoteSynthParam.NoteID = id.noteID();
1443     e.Param.NoteSynthParam.Type = Event::synth_param_volume;
1444     e.Param.NoteSynthParam.Delta = 0.f;
1445     e.Param.NoteSynthParam.Relative = false;
1446    
1447     // scheduling with 0 delay would also work here, but +1 is more
1448     // safe regarding potential future implementation changes of the
1449     // scheduler (see API comments of RTAVLTree::insert())
1450     pEngineChannel->ScheduleEventMicroSec(&e, 1);
1451     }
1452     // and finally if stopping the note was requested after the fade out
1453     // completed, then schedule to kill the voice after the requested
1454     // duration
1455     if (stop) {
1456     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1457     e.Init(); // clear IDs
1458     e.Type = Event::type_kill_note;
1459     e.Param.Note.ID = id.noteID();
1460     e.Param.Note.Key = pNote->hostKey;
1461    
1462     pEngineChannel->ScheduleEventMicroSec(&e, duration + 1);
1463     }
1464     }
1465     }
1466    
1467     return successResult();
1468     }
1469    
1470 schoenebeck 3193 // get_event_par() function
1471    
1472     InstrumentScriptVMFunction_get_event_par::InstrumentScriptVMFunction_get_event_par(InstrumentScriptVM* parent)
1473     : m_vm(parent)
1474     {
1475     }
1476    
1477     VMFnResult* InstrumentScriptVMFunction_get_event_par::exec(VMFnArgs* args) {
1478     AbstractEngineChannel* pEngineChannel =
1479     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1480    
1481     const ScriptID id = args->arg(0)->asInt()->evalInt();
1482     if (!id) {
1483     wrnMsg("get_event_par(): note ID for argument 1 may not be zero");
1484     return successResult(0);
1485     }
1486     if (!id.isNoteID()) {
1487     wrnMsg("get_event_par(): argument 1 is not a note ID");
1488     return successResult(0);
1489     }
1490    
1491     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1492     if (!pNote) {
1493     wrnMsg("get_event_par(): no note alive with that note ID of argument 1");
1494     return successResult(0);
1495     }
1496    
1497     const int parameter = args->arg(1)->asInt()->evalInt();
1498     switch (parameter) {
1499     case EVENT_PAR_NOTE:
1500     return successResult(pNote->cause.Param.Note.Key);
1501     case EVENT_PAR_VELOCITY:
1502     return successResult(pNote->cause.Param.Note.Velocity);
1503     case EVENT_PAR_VOLUME:
1504     return successResult(
1505     RTMath::LinRatioToDecibel(pNote->Override.Volume) * 1000.f
1506     );
1507     case EVENT_PAR_TUNE:
1508     return successResult(
1509     RTMath::FreqRatioToCents(pNote->Override.Pitch) * 1000.f
1510     );
1511     case EVENT_PAR_0:
1512     return successResult(pNote->userPar[0]);
1513     case EVENT_PAR_1:
1514     return successResult(pNote->userPar[1]);
1515     case EVENT_PAR_2:
1516     return successResult(pNote->userPar[2]);
1517     case EVENT_PAR_3:
1518     return successResult(pNote->userPar[3]);
1519     }
1520    
1521     wrnMsg("get_event_par(): argument 2 is an invalid event parameter");
1522     return successResult(0);
1523     }
1524    
1525     // set_event_par() function
1526    
1527     InstrumentScriptVMFunction_set_event_par::InstrumentScriptVMFunction_set_event_par(InstrumentScriptVM* parent)
1528     : m_vm(parent)
1529     {
1530     }
1531    
1532     VMFnResult* InstrumentScriptVMFunction_set_event_par::exec(VMFnArgs* args) {
1533     AbstractEngineChannel* pEngineChannel =
1534     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1535    
1536     const ScriptID id = args->arg(0)->asInt()->evalInt();
1537     if (!id) {
1538     wrnMsg("set_event_par(): note ID for argument 1 may not be zero");
1539     return successResult();
1540     }
1541     if (!id.isNoteID()) {
1542     wrnMsg("set_event_par(): argument 1 is not a note ID");
1543     return successResult();
1544     }
1545    
1546     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1547     if (!pNote) return successResult();
1548    
1549     const int parameter = args->arg(1)->asInt()->evalInt();
1550     const int value = args->arg(2)->asInt()->evalInt();
1551    
1552     switch (parameter) {
1553     case EVENT_PAR_NOTE:
1554     if (value < 0 || value > 127) {
1555     wrnMsg("set_event_par(): note number of argument 3 is out of range");
1556     return successResult();
1557     }
1558 schoenebeck 3214 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1559 schoenebeck 3193 pNote->cause.Param.Note.Key = value;
1560 schoenebeck 3214 m_vm->m_event->cause.Param.Note.Key = value;
1561     } else {
1562 schoenebeck 3193 wrnMsg("set_event_par(): note number can only be changed when note is new");
1563 schoenebeck 3214 }
1564 schoenebeck 3193 return successResult();
1565     case EVENT_PAR_VELOCITY:
1566     if (value < 0 || value > 127) {
1567     wrnMsg("set_event_par(): velocity of argument 3 is out of range");
1568     return successResult();
1569     }
1570 schoenebeck 3214 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1571 schoenebeck 3193 pNote->cause.Param.Note.Velocity = value;
1572 schoenebeck 3214 m_vm->m_event->cause.Param.Note.Velocity = value;
1573     } else {
1574 schoenebeck 3193 wrnMsg("set_event_par(): velocity can only be changed when note is new");
1575 schoenebeck 3214 }
1576 schoenebeck 3193 return successResult();
1577     case EVENT_PAR_VOLUME:
1578     wrnMsg("set_event_par(): changing volume by this function is currently not supported, use change_vol() instead");
1579     return successResult();
1580     case EVENT_PAR_TUNE:
1581     wrnMsg("set_event_par(): changing tune by this function is currently not supported, use change_tune() instead");
1582     return successResult();
1583     case EVENT_PAR_0:
1584     pNote->userPar[0] = value;
1585     return successResult();
1586     case EVENT_PAR_1:
1587     pNote->userPar[1] = value;
1588     return successResult();
1589     case EVENT_PAR_2:
1590     pNote->userPar[2] = value;
1591     return successResult();
1592     case EVENT_PAR_3:
1593     pNote->userPar[3] = value;
1594     return successResult();
1595     }
1596    
1597     wrnMsg("set_event_par(): argument 2 is an invalid event parameter");
1598     return successResult();
1599     }
1600    
1601 schoenebeck 3214 // change_note() function
1602    
1603     InstrumentScriptVMFunction_change_note::InstrumentScriptVMFunction_change_note(InstrumentScriptVM* parent)
1604     : m_vm(parent)
1605     {
1606     }
1607    
1608     VMFnResult* InstrumentScriptVMFunction_change_note::exec(VMFnArgs* args) {
1609     AbstractEngineChannel* pEngineChannel =
1610     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1611    
1612     const ScriptID id = args->arg(0)->asInt()->evalInt();
1613     if (!id) {
1614     wrnMsg("change_note(): note ID for argument 1 may not be zero");
1615     return successResult();
1616     }
1617     if (!id.isNoteID()) {
1618     wrnMsg("change_note(): argument 1 is not a note ID");
1619     return successResult();
1620     }
1621    
1622     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1623     if (!pNote) return successResult();
1624    
1625     const int value = args->arg(1)->asInt()->evalInt();
1626     if (value < 0 || value > 127) {
1627     wrnMsg("change_note(): note number of argument 2 is out of range");
1628     return successResult();
1629     }
1630    
1631     if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1632     pNote->cause.Param.Note.Key = value;
1633     m_vm->m_event->cause.Param.Note.Key = value;
1634 schoenebeck 3216 } else {
1635 schoenebeck 3214 wrnMsg("change_note(): note number can only be changed when note is new");
1636     }
1637    
1638     return successResult();
1639     }
1640    
1641     // change_velo() function
1642    
1643     InstrumentScriptVMFunction_change_velo::InstrumentScriptVMFunction_change_velo(InstrumentScriptVM* parent)
1644     : m_vm(parent)
1645     {
1646     }
1647    
1648     VMFnResult* InstrumentScriptVMFunction_change_velo::exec(VMFnArgs* args) {
1649     AbstractEngineChannel* pEngineChannel =
1650     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1651    
1652     const ScriptID id = args->arg(0)->asInt()->evalInt();
1653     if (!id) {
1654     wrnMsg("change_velo(): note ID for argument 1 may not be zero");
1655     return successResult();
1656     }
1657     if (!id.isNoteID()) {
1658     wrnMsg("change_velo(): argument 1 is not a note ID");
1659     return successResult();
1660     }
1661    
1662     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1663     if (!pNote) return successResult();
1664    
1665     const int value = args->arg(1)->asInt()->evalInt();
1666     if (value < 0 || value > 127) {
1667     wrnMsg("change_velo(): velocity of argument 2 is out of range");
1668     return successResult();
1669     }
1670    
1671     if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1672     pNote->cause.Param.Note.Velocity = value;
1673     m_vm->m_event->cause.Param.Note.Velocity = value;
1674 schoenebeck 3216 } else {
1675 schoenebeck 3214 wrnMsg("change_velo(): velocity can only be changed when note is new");
1676     }
1677    
1678     return successResult();
1679     }
1680    
1681 schoenebeck 2935 // event_status() function
1682    
1683     InstrumentScriptVMFunction_event_status::InstrumentScriptVMFunction_event_status(InstrumentScriptVM* parent)
1684     : m_vm(parent)
1685     {
1686     }
1687    
1688     VMFnResult* InstrumentScriptVMFunction_event_status::exec(VMFnArgs* args) {
1689     AbstractEngineChannel* pEngineChannel =
1690     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1691    
1692     const ScriptID id = args->arg(0)->asInt()->evalInt();
1693     if (!id) {
1694     wrnMsg("event_status(): note ID for argument 1 may not be zero");
1695     return successResult(EVENT_STATUS_INACTIVE);
1696     }
1697     if (!id.isNoteID()) {
1698     wrnMsg("event_status(): argument 1 is not a note ID");
1699     return successResult(EVENT_STATUS_INACTIVE);
1700     }
1701    
1702     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1703     return successResult(pNote ? EVENT_STATUS_NOTE_QUEUE : EVENT_STATUS_INACTIVE);
1704     }
1705    
1706 schoenebeck 2948 // wait() function (overrides core wait() implementation)
1707    
1708     InstrumentScriptVMFunction_wait::InstrumentScriptVMFunction_wait(InstrumentScriptVM* parent)
1709     : CoreVMFunction_wait(parent)
1710     {
1711     }
1712    
1713     VMFnResult* InstrumentScriptVMFunction_wait::exec(VMFnArgs* args) {
1714     InstrumentScriptVM* m_vm = (InstrumentScriptVM*) vm;
1715    
1716     // this might be set by passing 1 with the 2nd argument of built-in stop_wait() function
1717     if (m_vm->m_event->ignoreAllWaitCalls) return successResult();
1718    
1719     return CoreVMFunction_wait::exec(args);
1720     }
1721    
1722     // stop_wait() function
1723    
1724     InstrumentScriptVMFunction_stop_wait::InstrumentScriptVMFunction_stop_wait(InstrumentScriptVM* parent)
1725     : m_vm(parent)
1726     {
1727     }
1728    
1729     VMFnResult* InstrumentScriptVMFunction_stop_wait::exec(VMFnArgs* args) {
1730     AbstractEngineChannel* pEngineChannel =
1731     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1732    
1733     const script_callback_id_t id = args->arg(0)->asInt()->evalInt();
1734     if (!id) {
1735     wrnMsg("stop_wait(): callback ID for argument 1 may not be zero");
1736     return successResult();
1737     }
1738    
1739     RTList<ScriptEvent>::Iterator itCallback = pEngineChannel->ScriptCallbackByID(id);
1740     if (!itCallback) return successResult(); // ignore if callback is i.e. not alive anymore
1741    
1742     const bool disableWaitForever =
1743     (args->argsCount() >= 2) ? (args->arg(1)->asInt()->evalInt() == 1) : false;
1744    
1745     pEngineChannel->ScheduleResumeOfScriptCallback(
1746 schoenebeck 3205 itCallback, m_vm->m_event->scheduleTime, disableWaitForever
1747 schoenebeck 2948 );
1748    
1749     return successResult();
1750     }
1751    
1752 schoenebeck 2596 } // namespace LinuxSampler

  ViewVC Help
Powered by ViewVC