/[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 3246 - (hide annotations) (download)
Sun May 28 22:22:56 2017 UTC (6 years, 10 months ago) by schoenebeck
File size: 81027 byte(s)
* NKSP: Implemented built-in script function "change_vol_curve()".
* NKSP: Implemented built-in script function "change_tune_curve()".
* NKSP: Added built-in script constant "$NKSP_LINEAR".
* NKSP: Added built-in script constant "$NKSP_EASE_IN_EASE_OUT".
* Bumped version (2.0.0.svn54).

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 schoenebeck 3218 // then immediately apply the volume to note object, but only if
389     // change_vol_time() has not been called before
390     if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime &&
391     pNote->Override.VolumeTime <= DEFAULT_NOTE_VOLUME_TIME_S)
392     {
393 schoenebeck 2962 if (relative)
394 schoenebeck 2931 pNote->Override.Volume *= fVolumeLin;
395     else
396 schoenebeck 2962 pNote->Override.Volume = fVolumeLin;
397     } else { // otherwise schedule the volume change ...
398 schoenebeck 2931 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
399     e.Init(); // clear IDs
400     e.Type = Event::type_note_synth_param;
401     e.Param.NoteSynthParam.NoteID = id.noteID();
402     e.Param.NoteSynthParam.Type = Event::synth_param_volume;
403     e.Param.NoteSynthParam.Delta = fVolumeLin;
404     e.Param.NoteSynthParam.Relative = relative;
405    
406     pEngineChannel->ScheduleEventMicroSec(&e, 0);
407     }
408 schoenebeck 2962 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
409     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
410     for (int i = 0; i < ids->arraySize(); ++i) {
411     const ScriptID id = ids->evalIntElement(i);
412     if (!id || !id.isNoteID()) continue;
413    
414     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
415     if (!pNote) continue;
416    
417     // if change_vol() was called immediately after note was triggered
418 schoenebeck 3218 // then immediately apply the volume to Note object, but only if
419     // change_vol_time() has not been called before
420     if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime &&
421     pNote->Override.VolumeTime <= DEFAULT_NOTE_VOLUME_TIME_S)
422     {
423 schoenebeck 2962 if (relative)
424     pNote->Override.Volume *= fVolumeLin;
425     else
426     pNote->Override.Volume = fVolumeLin;
427     } else { // otherwise schedule the volume change ...
428     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
429     e.Init(); // clear IDs
430     e.Type = Event::type_note_synth_param;
431     e.Param.NoteSynthParam.NoteID = id.noteID();
432     e.Param.NoteSynthParam.Type = Event::synth_param_volume;
433     e.Param.NoteSynthParam.Delta = fVolumeLin;
434     e.Param.NoteSynthParam.Relative = relative;
435    
436     pEngineChannel->ScheduleEventMicroSec(&e, 0);
437     }
438     }
439 schoenebeck 2931 }
440    
441     return successResult();
442     }
443    
444     // change_tune() function
445    
446     InstrumentScriptVMFunction_change_tune::InstrumentScriptVMFunction_change_tune(InstrumentScriptVM* parent)
447     : m_vm(parent)
448     {
449     }
450    
451     bool InstrumentScriptVMFunction_change_tune::acceptsArgType(int iArg, ExprType_t type) const {
452     if (iArg == 0)
453     return type == INT_EXPR || type == INT_ARR_EXPR;
454     else
455 schoenebeck 3188 return type == INT_EXPR;
456 schoenebeck 2931 }
457    
458     VMFnResult* InstrumentScriptVMFunction_change_tune::exec(VMFnArgs* args) {
459     int tune = args->arg(1)->asInt()->evalInt(); // tuning change in milli cents
460     bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;
461 schoenebeck 2962 const float fFreqRatio = RTMath::CentsToFreqRatioUnlimited(float(tune) / 1000.f);
462 schoenebeck 2931
463     AbstractEngineChannel* pEngineChannel =
464     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
465    
466     if (args->arg(0)->exprType() == INT_EXPR) {
467     const ScriptID id = args->arg(0)->asInt()->evalInt();
468     if (!id) {
469     wrnMsg("change_tune(): note ID for argument 1 may not be zero");
470     return successResult();
471     }
472     if (!id.isNoteID()) {
473     wrnMsg("change_tune(): argument 1 is not a note ID");
474     return successResult();
475     }
476    
477     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
478     if (!pNote) return successResult();
479    
480 schoenebeck 2962 // if change_tune() was called immediately after note was triggered
481 schoenebeck 3218 // then immediately apply the tuning to Note object, but only if
482     // change_tune_time() has not been called before
483     if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime &&
484     pNote->Override.PitchTime <= DEFAULT_NOTE_PITCH_TIME_S)
485     {
486 schoenebeck 2962 if (relative)
487 schoenebeck 2931 pNote->Override.Pitch *= fFreqRatio;
488     else
489 schoenebeck 2962 pNote->Override.Pitch = fFreqRatio;
490     } else { // otherwise schedule tuning change ...
491 schoenebeck 2931 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
492     e.Init(); // clear IDs
493     e.Type = Event::type_note_synth_param;
494     e.Param.NoteSynthParam.NoteID = id.noteID();
495     e.Param.NoteSynthParam.Type = Event::synth_param_pitch;
496     e.Param.NoteSynthParam.Delta = fFreqRatio;
497     e.Param.NoteSynthParam.Relative = relative;
498    
499     pEngineChannel->ScheduleEventMicroSec(&e, 0);
500     }
501 schoenebeck 2962 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
502     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
503     for (int i = 0; i < ids->arraySize(); ++i) {
504     const ScriptID id = ids->evalIntElement(i);
505     if (!id || !id.isNoteID()) continue;
506    
507     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
508     if (!pNote) continue;
509    
510     // if change_tune() was called immediately after note was triggered
511 schoenebeck 3218 // then immediately apply the tuning to Note object, but only if
512     // change_tune_time() has not been called before
513     if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime &&
514     pNote->Override.PitchTime <= DEFAULT_NOTE_PITCH_TIME_S)
515     {
516 schoenebeck 2962 if (relative)
517     pNote->Override.Pitch *= fFreqRatio;
518     else
519     pNote->Override.Pitch = fFreqRatio;
520     } else { // otherwise schedule tuning change ...
521     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
522     e.Init(); // clear IDs
523     e.Type = Event::type_note_synth_param;
524     e.Param.NoteSynthParam.NoteID = id.noteID();
525     e.Param.NoteSynthParam.Type = Event::synth_param_pitch;
526     e.Param.NoteSynthParam.Delta = fFreqRatio;
527     e.Param.NoteSynthParam.Relative = relative;
528    
529     pEngineChannel->ScheduleEventMicroSec(&e, 0);
530     }
531     }
532 schoenebeck 2931 }
533    
534     return successResult();
535     }
536    
537     // change_pan() function
538    
539     InstrumentScriptVMFunction_change_pan::InstrumentScriptVMFunction_change_pan(InstrumentScriptVM* parent)
540     : m_vm(parent)
541     {
542     }
543    
544     bool InstrumentScriptVMFunction_change_pan::acceptsArgType(int iArg, ExprType_t type) const {
545     if (iArg == 0)
546     return type == INT_EXPR || type == INT_ARR_EXPR;
547     else
548 schoenebeck 3188 return type == INT_EXPR;
549 schoenebeck 2931 }
550    
551     VMFnResult* InstrumentScriptVMFunction_change_pan::exec(VMFnArgs* args) {
552     int pan = args->arg(1)->asInt()->evalInt();
553     bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;
554    
555     if (pan > 1000) {
556     wrnMsg("change_pan(): argument 2 may not be larger than 1000");
557     pan = 1000;
558     } else if (pan < -1000) {
559     wrnMsg("change_pan(): argument 2 may not be smaller than -1000");
560     pan = -1000;
561     }
562 schoenebeck 2962 const float fPan = float(pan) / 1000.f;
563 schoenebeck 2931
564     AbstractEngineChannel* pEngineChannel =
565     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
566    
567     if (args->arg(0)->exprType() == INT_EXPR) {
568     const ScriptID id = args->arg(0)->asInt()->evalInt();
569     if (!id) {
570     wrnMsg("change_pan(): note ID for argument 1 may not be zero");
571     return successResult();
572     }
573     if (!id.isNoteID()) {
574     wrnMsg("change_pan(): argument 1 is not a note ID");
575     return successResult();
576     }
577    
578     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
579     if (!pNote) return successResult();
580    
581 schoenebeck 2962 // if change_pan() was called immediately after note was triggered
582     // then immediately apply the panning to Note object
583 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
584 schoenebeck 2962 if (relative) {
585 schoenebeck 2931 pNote->Override.Pan = RTMath::RelativeSummedAvg(pNote->Override.Pan, fPan, ++pNote->Override.PanSources);
586     } else {
587     pNote->Override.Pan = fPan;
588     pNote->Override.PanSources = 1; // only relevant on subsequent change_pan() calls on same note with 'relative' being set
589 schoenebeck 2962 }
590     } else { // otherwise schedule panning change ...
591 schoenebeck 2931 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
592     e.Init(); // clear IDs
593     e.Type = Event::type_note_synth_param;
594     e.Param.NoteSynthParam.NoteID = id.noteID();
595     e.Param.NoteSynthParam.Type = Event::synth_param_pan;
596     e.Param.NoteSynthParam.Delta = fPan;
597     e.Param.NoteSynthParam.Relative = relative;
598    
599     pEngineChannel->ScheduleEventMicroSec(&e, 0);
600     }
601 schoenebeck 2962 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
602     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
603     for (int i = 0; i < ids->arraySize(); ++i) {
604     const ScriptID id = ids->evalIntElement(i);
605     if (!id || !id.isNoteID()) continue;
606    
607     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
608     if (!pNote) continue;
609    
610     // if change_pan() was called immediately after note was triggered
611     // then immediately apply the panning to Note object
612 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
613 schoenebeck 2962 if (relative) {
614     pNote->Override.Pan = RTMath::RelativeSummedAvg(pNote->Override.Pan, fPan, ++pNote->Override.PanSources);
615     } else {
616     pNote->Override.Pan = fPan;
617     pNote->Override.PanSources = 1; // only relevant on subsequent change_pan() calls on same note with 'relative' being set
618     }
619     } else { // otherwise schedule panning change ...
620     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
621     e.Init(); // clear IDs
622     e.Type = Event::type_note_synth_param;
623     e.Param.NoteSynthParam.NoteID = id.noteID();
624     e.Param.NoteSynthParam.Type = Event::synth_param_pan;
625     e.Param.NoteSynthParam.Delta = fPan;
626     e.Param.NoteSynthParam.Relative = relative;
627    
628     pEngineChannel->ScheduleEventMicroSec(&e, 0);
629     }
630     }
631 schoenebeck 2931 }
632    
633     return successResult();
634     }
635    
636 schoenebeck 2935 #define VM_FILTER_PAR_MAX_VALUE 1000000
637 schoenebeck 2953 #define VM_EG_PAR_MAX_VALUE 1000000
638 schoenebeck 2935
639     // change_cutoff() function
640    
641     InstrumentScriptVMFunction_change_cutoff::InstrumentScriptVMFunction_change_cutoff(InstrumentScriptVM* parent)
642     : m_vm(parent)
643     {
644     }
645    
646     bool InstrumentScriptVMFunction_change_cutoff::acceptsArgType(int iArg, ExprType_t type) const {
647     if (iArg == 0)
648     return type == INT_EXPR || type == INT_ARR_EXPR;
649     else
650 schoenebeck 3188 return type == INT_EXPR;
651 schoenebeck 2935 }
652    
653     VMFnResult* InstrumentScriptVMFunction_change_cutoff::exec(VMFnArgs* args) {
654     int cutoff = args->arg(1)->asInt()->evalInt();
655     if (cutoff > VM_FILTER_PAR_MAX_VALUE) {
656     wrnMsg("change_cutoff(): argument 2 may not be larger than 1000000");
657     cutoff = VM_FILTER_PAR_MAX_VALUE;
658     } else if (cutoff < 0) {
659     wrnMsg("change_cutoff(): argument 2 may not be negative");
660     cutoff = 0;
661     }
662 schoenebeck 2962 const float fCutoff = float(cutoff) / float(VM_FILTER_PAR_MAX_VALUE);
663 schoenebeck 2935
664     AbstractEngineChannel* pEngineChannel =
665     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
666    
667     if (args->arg(0)->exprType() == INT_EXPR) {
668     const ScriptID id = args->arg(0)->asInt()->evalInt();
669     if (!id) {
670     wrnMsg("change_cutoff(): note ID for argument 1 may not be zero");
671     return successResult();
672     }
673     if (!id.isNoteID()) {
674     wrnMsg("change_cutoff(): argument 1 is not a note ID");
675     return successResult();
676     }
677    
678     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
679     if (!pNote) return successResult();
680    
681 schoenebeck 2962 // if change_cutoff() was called immediately after note was triggered
682     // then immediately apply cutoff to Note object
683 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
684 schoenebeck 2962 pNote->Override.Cutoff = fCutoff;
685     } else { // otherwise schedule cutoff change ...
686     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
687     e.Init(); // clear IDs
688     e.Type = Event::type_note_synth_param;
689     e.Param.NoteSynthParam.NoteID = id.noteID();
690     e.Param.NoteSynthParam.Type = Event::synth_param_cutoff;
691     e.Param.NoteSynthParam.Delta = fCutoff;
692     e.Param.NoteSynthParam.Relative = false;
693 schoenebeck 2935
694 schoenebeck 2962 pEngineChannel->ScheduleEventMicroSec(&e, 0);
695     }
696 schoenebeck 2935 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
697     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
698     for (int i = 0; i < ids->arraySize(); ++i) {
699     const ScriptID id = ids->evalIntElement(i);
700     if (!id || !id.isNoteID()) continue;
701    
702     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
703     if (!pNote) continue;
704    
705 schoenebeck 2962 // if change_cutoff() was called immediately after note was triggered
706     // then immediately apply cutoff to Note object
707 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
708 schoenebeck 2962 pNote->Override.Cutoff = fCutoff;
709     } else { // otherwise schedule cutoff change ...
710     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
711     e.Init(); // clear IDs
712     e.Type = Event::type_note_synth_param;
713     e.Param.NoteSynthParam.NoteID = id.noteID();
714     e.Param.NoteSynthParam.Type = Event::synth_param_cutoff;
715     e.Param.NoteSynthParam.Delta = fCutoff;
716     e.Param.NoteSynthParam.Relative = false;
717 schoenebeck 2935
718 schoenebeck 2962 pEngineChannel->ScheduleEventMicroSec(&e, 0);
719     }
720 schoenebeck 2935 }
721     }
722    
723     return successResult();
724     }
725    
726     // change_reso() function
727    
728     InstrumentScriptVMFunction_change_reso::InstrumentScriptVMFunction_change_reso(InstrumentScriptVM* parent)
729     : m_vm(parent)
730     {
731     }
732    
733     bool InstrumentScriptVMFunction_change_reso::acceptsArgType(int iArg, ExprType_t type) const {
734     if (iArg == 0)
735     return type == INT_EXPR || type == INT_ARR_EXPR;
736     else
737 schoenebeck 3188 return type == INT_EXPR;
738 schoenebeck 2935 }
739    
740     VMFnResult* InstrumentScriptVMFunction_change_reso::exec(VMFnArgs* args) {
741     int resonance = args->arg(1)->asInt()->evalInt();
742     if (resonance > VM_FILTER_PAR_MAX_VALUE) {
743     wrnMsg("change_reso(): argument 2 may not be larger than 1000000");
744     resonance = VM_FILTER_PAR_MAX_VALUE;
745     } else if (resonance < 0) {
746     wrnMsg("change_reso(): argument 2 may not be negative");
747     resonance = 0;
748     }
749 schoenebeck 2962 const float fResonance = float(resonance) / float(VM_FILTER_PAR_MAX_VALUE);
750 schoenebeck 2935
751     AbstractEngineChannel* pEngineChannel =
752     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
753    
754     if (args->arg(0)->exprType() == INT_EXPR) {
755     const ScriptID id = args->arg(0)->asInt()->evalInt();
756     if (!id) {
757     wrnMsg("change_reso(): note ID for argument 1 may not be zero");
758     return successResult();
759     }
760     if (!id.isNoteID()) {
761     wrnMsg("change_reso(): argument 1 is not a note ID");
762     return successResult();
763     }
764    
765     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
766     if (!pNote) return successResult();
767    
768 schoenebeck 2962 // if change_reso() was called immediately after note was triggered
769     // then immediately apply resonance to Note object
770 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
771 schoenebeck 2962 pNote->Override.Resonance = fResonance;
772     } else { // otherwise schedule resonance change ...
773     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
774     e.Init(); // clear IDs
775     e.Type = Event::type_note_synth_param;
776     e.Param.NoteSynthParam.NoteID = id.noteID();
777     e.Param.NoteSynthParam.Type = Event::synth_param_resonance;
778     e.Param.NoteSynthParam.Delta = fResonance;
779     e.Param.NoteSynthParam.Relative = false;
780 schoenebeck 2935
781 schoenebeck 2962 pEngineChannel->ScheduleEventMicroSec(&e, 0);
782     }
783 schoenebeck 2935 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
784     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
785     for (int i = 0; i < ids->arraySize(); ++i) {
786     const ScriptID id = ids->evalIntElement(i);
787     if (!id || !id.isNoteID()) continue;
788    
789     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
790     if (!pNote) continue;
791    
792 schoenebeck 2962 // if change_reso() was called immediately after note was triggered
793     // then immediately apply resonance to Note object
794 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
795 schoenebeck 2962 pNote->Override.Resonance = fResonance;
796     } else { // otherwise schedule resonance change ...
797     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
798     e.Init(); // clear IDs
799     e.Type = Event::type_note_synth_param;
800     e.Param.NoteSynthParam.NoteID = id.noteID();
801     e.Param.NoteSynthParam.Type = Event::synth_param_resonance;
802     e.Param.NoteSynthParam.Delta = fResonance;
803     e.Param.NoteSynthParam.Relative = false;
804 schoenebeck 2935
805 schoenebeck 2962 pEngineChannel->ScheduleEventMicroSec(&e, 0);
806     }
807 schoenebeck 2935 }
808     }
809    
810     return successResult();
811     }
812 schoenebeck 2953
813     // change_attack() function
814 schoenebeck 2935
815 schoenebeck 2953 InstrumentScriptVMFunction_change_attack::InstrumentScriptVMFunction_change_attack(InstrumentScriptVM* parent)
816     : m_vm(parent)
817     {
818     }
819    
820     bool InstrumentScriptVMFunction_change_attack::acceptsArgType(int iArg, ExprType_t type) const {
821     if (iArg == 0)
822     return type == INT_EXPR || type == INT_ARR_EXPR;
823     else
824 schoenebeck 3188 return type == INT_EXPR;
825 schoenebeck 2953 }
826    
827     VMFnResult* InstrumentScriptVMFunction_change_attack::exec(VMFnArgs* args) {
828     int attack = args->arg(1)->asInt()->evalInt();
829     if (attack > VM_EG_PAR_MAX_VALUE) {
830     wrnMsg("change_attack(): argument 2 may not be larger than 1000000");
831     attack = VM_EG_PAR_MAX_VALUE;
832     } else if (attack < 0) {
833     wrnMsg("change_attack(): argument 2 may not be negative");
834     attack = 0;
835     }
836     const float fAttack = float(attack) / float(VM_EG_PAR_MAX_VALUE);
837    
838     AbstractEngineChannel* pEngineChannel =
839     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
840    
841     if (args->arg(0)->exprType() == INT_EXPR) {
842     const ScriptID id = args->arg(0)->asInt()->evalInt();
843     if (!id) {
844     wrnMsg("change_attack(): note ID for argument 1 may not be zero");
845     return successResult();
846     }
847     if (!id.isNoteID()) {
848     wrnMsg("change_attack(): argument 1 is not a note ID");
849     return successResult();
850     }
851    
852     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
853 schoenebeck 2962 if (!pNote) return successResult();
854 schoenebeck 2953
855 schoenebeck 2962 // if change_attack() was called immediately after note was triggered
856     // then immediately apply attack to Note object
857 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
858 schoenebeck 2962 pNote->Override.Attack = fAttack;
859     } else { // otherwise schedule attack change ...
860     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
861     e.Init(); // clear IDs
862     e.Type = Event::type_note_synth_param;
863     e.Param.NoteSynthParam.NoteID = id.noteID();
864     e.Param.NoteSynthParam.Type = Event::synth_param_attack;
865     e.Param.NoteSynthParam.Delta = fAttack;
866     e.Param.NoteSynthParam.Relative = false;
867 schoenebeck 2953
868 schoenebeck 2962 pEngineChannel->ScheduleEventMicroSec(&e, 0);
869     }
870 schoenebeck 2953 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
871     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
872     for (int i = 0; i < ids->arraySize(); ++i) {
873     const ScriptID id = ids->evalIntElement(i);
874     if (!id || !id.isNoteID()) continue;
875    
876     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
877     if (!pNote) continue;
878    
879 schoenebeck 2962 // if change_attack() was called immediately after note was triggered
880     // then immediately apply attack to Note object
881 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
882 schoenebeck 2962 pNote->Override.Attack = fAttack;
883     } else { // otherwise schedule attack change ...
884     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
885     e.Init(); // clear IDs
886     e.Type = Event::type_note_synth_param;
887     e.Param.NoteSynthParam.NoteID = id.noteID();
888     e.Param.NoteSynthParam.Type = Event::synth_param_attack;
889     e.Param.NoteSynthParam.Delta = fAttack;
890     e.Param.NoteSynthParam.Relative = false;
891 schoenebeck 2953
892 schoenebeck 2962 pEngineChannel->ScheduleEventMicroSec(&e, 0);
893     }
894 schoenebeck 2953 }
895     }
896    
897     return successResult();
898     }
899    
900     // change_decay() function
901    
902     InstrumentScriptVMFunction_change_decay::InstrumentScriptVMFunction_change_decay(InstrumentScriptVM* parent)
903     : m_vm(parent)
904     {
905     }
906    
907     bool InstrumentScriptVMFunction_change_decay::acceptsArgType(int iArg, ExprType_t type) const {
908     if (iArg == 0)
909     return type == INT_EXPR || type == INT_ARR_EXPR;
910     else
911 schoenebeck 3188 return type == INT_EXPR;
912 schoenebeck 2953 }
913    
914     VMFnResult* InstrumentScriptVMFunction_change_decay::exec(VMFnArgs* args) {
915     int decay = args->arg(1)->asInt()->evalInt();
916     if (decay > VM_EG_PAR_MAX_VALUE) {
917     wrnMsg("change_decay(): argument 2 may not be larger than 1000000");
918     decay = VM_EG_PAR_MAX_VALUE;
919     } else if (decay < 0) {
920     wrnMsg("change_decay(): argument 2 may not be negative");
921     decay = 0;
922     }
923     const float fDecay = float(decay) / float(VM_EG_PAR_MAX_VALUE);
924    
925     AbstractEngineChannel* pEngineChannel =
926     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
927    
928     if (args->arg(0)->exprType() == INT_EXPR) {
929     const ScriptID id = args->arg(0)->asInt()->evalInt();
930     if (!id) {
931     wrnMsg("change_decay(): note ID for argument 1 may not be zero");
932     return successResult();
933     }
934     if (!id.isNoteID()) {
935     wrnMsg("change_decay(): argument 1 is not a note ID");
936     return successResult();
937     }
938    
939     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
940 schoenebeck 2962 if (!pNote) return successResult();
941 schoenebeck 2953
942 schoenebeck 2962 // if change_decay() was called immediately after note was triggered
943     // then immediately apply decay to Note object
944 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
945 schoenebeck 2962 pNote->Override.Decay = fDecay;
946     } else { // otherwise schedule decay change ...
947     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
948     e.Init(); // clear IDs
949     e.Type = Event::type_note_synth_param;
950     e.Param.NoteSynthParam.NoteID = id.noteID();
951     e.Param.NoteSynthParam.Type = Event::synth_param_decay;
952     e.Param.NoteSynthParam.Delta = fDecay;
953     e.Param.NoteSynthParam.Relative = false;
954 schoenebeck 2953
955 schoenebeck 2962 pEngineChannel->ScheduleEventMicroSec(&e, 0);
956     }
957 schoenebeck 2953 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
958     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
959     for (int i = 0; i < ids->arraySize(); ++i) {
960     const ScriptID id = ids->evalIntElement(i);
961     if (!id || !id.isNoteID()) continue;
962    
963     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
964     if (!pNote) continue;
965    
966 schoenebeck 2962 // if change_decay() was called immediately after note was triggered
967     // then immediately apply decay to Note object
968 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
969 schoenebeck 2962 pNote->Override.Decay = fDecay;
970     } else { // otherwise schedule decay change ...
971     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
972     e.Init(); // clear IDs
973     e.Type = Event::type_note_synth_param;
974     e.Param.NoteSynthParam.NoteID = id.noteID();
975     e.Param.NoteSynthParam.Type = Event::synth_param_decay;
976     e.Param.NoteSynthParam.Delta = fDecay;
977     e.Param.NoteSynthParam.Relative = false;
978 schoenebeck 2953
979 schoenebeck 2962 pEngineChannel->ScheduleEventMicroSec(&e, 0);
980     }
981 schoenebeck 2953 }
982     }
983    
984     return successResult();
985     }
986    
987     // change_release() function
988    
989     InstrumentScriptVMFunction_change_release::InstrumentScriptVMFunction_change_release(InstrumentScriptVM* parent)
990     : m_vm(parent)
991     {
992     }
993    
994     bool InstrumentScriptVMFunction_change_release::acceptsArgType(int iArg, ExprType_t type) const {
995     if (iArg == 0)
996     return type == INT_EXPR || type == INT_ARR_EXPR;
997     else
998 schoenebeck 3188 return type == INT_EXPR;
999 schoenebeck 2953 }
1000    
1001     VMFnResult* InstrumentScriptVMFunction_change_release::exec(VMFnArgs* args) {
1002     int release = args->arg(1)->asInt()->evalInt();
1003     if (release > VM_EG_PAR_MAX_VALUE) {
1004     wrnMsg("change_release(): argument 2 may not be larger than 1000000");
1005     release = VM_EG_PAR_MAX_VALUE;
1006     } else if (release < 0) {
1007     wrnMsg("change_release(): argument 2 may not be negative");
1008     release = 0;
1009     }
1010     const float fRelease = float(release) / float(VM_EG_PAR_MAX_VALUE);
1011    
1012     AbstractEngineChannel* pEngineChannel =
1013     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1014    
1015     if (args->arg(0)->exprType() == INT_EXPR) {
1016     const ScriptID id = args->arg(0)->asInt()->evalInt();
1017     if (!id) {
1018     wrnMsg("change_release(): note ID for argument 1 may not be zero");
1019     return successResult();
1020     }
1021     if (!id.isNoteID()) {
1022     wrnMsg("change_release(): argument 1 is not a note ID");
1023     return successResult();
1024     }
1025    
1026     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1027 schoenebeck 2962 if (!pNote) return successResult();
1028 schoenebeck 2953
1029 schoenebeck 2962 // if change_release() was called immediately after note was triggered
1030     // then immediately apply relase to Note object
1031 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1032 schoenebeck 2962 pNote->Override.Release = fRelease;
1033     } else { // otherwise schedule release change ...
1034     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1035     e.Init(); // clear IDs
1036     e.Type = Event::type_note_synth_param;
1037     e.Param.NoteSynthParam.NoteID = id.noteID();
1038     e.Param.NoteSynthParam.Type = Event::synth_param_release;
1039     e.Param.NoteSynthParam.Delta = fRelease;
1040     e.Param.NoteSynthParam.Relative = false;
1041 schoenebeck 2953
1042 schoenebeck 2962 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1043     }
1044 schoenebeck 2953 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1045     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1046     for (int i = 0; i < ids->arraySize(); ++i) {
1047     const ScriptID id = ids->evalIntElement(i);
1048     if (!id || !id.isNoteID()) continue;
1049    
1050     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1051     if (!pNote) continue;
1052    
1053 schoenebeck 2962 // if change_release() was called immediately after note was triggered
1054     // then immediately apply relase to Note object
1055 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1056 schoenebeck 2962 pNote->Override.Release = fRelease;
1057     } else { // otherwise schedule release change ...
1058     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1059     e.Init(); // clear IDs
1060     e.Type = Event::type_note_synth_param;
1061     e.Param.NoteSynthParam.NoteID = id.noteID();
1062     e.Param.NoteSynthParam.Type = Event::synth_param_release;
1063     e.Param.NoteSynthParam.Delta = fRelease;
1064     e.Param.NoteSynthParam.Relative = false;
1065 schoenebeck 2953
1066 schoenebeck 2962 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1067     }
1068 schoenebeck 2953 }
1069     }
1070    
1071     return successResult();
1072     }
1073    
1074 schoenebeck 3246 // template for change_*() functions
1075    
1076 schoenebeck 3118 bool VMChangeSynthParamFunction::acceptsArgType(int iArg, ExprType_t type) const {
1077     if (iArg == 0)
1078     return type == INT_EXPR || type == INT_ARR_EXPR;
1079     else
1080 schoenebeck 3188 return type == INT_EXPR;
1081 schoenebeck 3118 }
1082    
1083 schoenebeck 3193 // Arbitrarily chosen constant value symbolizing "no limit".
1084     #define NO_LIMIT 1315916909
1085    
1086 schoenebeck 3188 template<float NoteBase::_Override::*T_noteParam, int T_synthParam,
1087     bool T_isNormalizedParam, int T_maxValue, int T_minValue>
1088 schoenebeck 3118 VMFnResult* VMChangeSynthParamFunction::execTemplate(VMFnArgs* args, const char* functionName) {
1089     int value = args->arg(1)->asInt()->evalInt();
1090 schoenebeck 3193 if (T_maxValue != NO_LIMIT && value > T_maxValue) {
1091 schoenebeck 3188 wrnMsg(String(functionName) + "(): argument 2 may not be larger than " + ToString(T_maxValue));
1092     value = T_maxValue;
1093 schoenebeck 3193 } else if (T_minValue != NO_LIMIT && value < T_minValue) {
1094 schoenebeck 3188 if (T_minValue == 0)
1095     wrnMsg(String(functionName) + "(): argument 2 may not be negative");
1096     else
1097     wrnMsg(String(functionName) + "(): argument 2 may not be smaller than " + ToString(T_minValue));
1098     value = T_minValue;
1099 schoenebeck 3118 }
1100 schoenebeck 3188 const float fValue = (T_isNormalizedParam) ?
1101     float(value) / float(T_maxValue) : // convert to 0.0 .. 1.0 value range
1102     float(value) / 1000000.f; // assuming microseconds here, convert to seconds
1103 schoenebeck 3118
1104     AbstractEngineChannel* pEngineChannel =
1105     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1106    
1107     if (args->arg(0)->exprType() == INT_EXPR) {
1108     const ScriptID id = args->arg(0)->asInt()->evalInt();
1109     if (!id) {
1110     wrnMsg(String(functionName) + "(): note ID for argument 1 may not be zero");
1111     return successResult();
1112     }
1113     if (!id.isNoteID()) {
1114     wrnMsg(String(functionName) + "(): argument 1 is not a note ID");
1115     return successResult();
1116     }
1117    
1118     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1119     if (!pNote) return successResult();
1120    
1121     // if this change_*() script function was called immediately after
1122     // note was triggered then immediately apply the synth parameter
1123     // change to Note object
1124 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1125 schoenebeck 3118 pNote->Override.*T_noteParam = fValue;
1126     } else { // otherwise schedule this synth parameter change ...
1127     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1128     e.Init(); // clear IDs
1129     e.Type = Event::type_note_synth_param;
1130     e.Param.NoteSynthParam.NoteID = id.noteID();
1131     e.Param.NoteSynthParam.Type = (Event::synth_param_t) T_synthParam;
1132     e.Param.NoteSynthParam.Delta = fValue;
1133     e.Param.NoteSynthParam.Relative = false;
1134    
1135     pEngineChannel->ScheduleEventMicroSec(&e, 0);
1136     }
1137     } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1138     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1139     for (int i = 0; i < ids->arraySize(); ++i) {
1140     const ScriptID id = ids->evalIntElement(i);
1141     if (!id || !id.isNoteID()) continue;
1142    
1143     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1144     if (!pNote) continue;
1145    
1146     // if this change_*() script function was called immediately after
1147     // note was triggered then immediately apply the synth parameter
1148     // change to Note object
1149 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1150 schoenebeck 3118 pNote->Override.*T_noteParam = fValue;
1151     } else { // otherwise schedule this synth parameter change ...
1152     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1153     e.Init(); // clear IDs
1154     e.Type = Event::type_note_synth_param;
1155     e.Param.NoteSynthParam.NoteID = id.noteID();
1156     e.Param.NoteSynthParam.Type = (Event::synth_param_t) T_synthParam;
1157     e.Param.NoteSynthParam.Delta = fValue;
1158     e.Param.NoteSynthParam.Relative = false;
1159    
1160     pEngineChannel->ScheduleEventMicroSec(&e, 0);
1161     }
1162     }
1163     }
1164    
1165     return successResult();
1166     }
1167    
1168     // change_amp_lfo_depth() function
1169    
1170     VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_depth::exec(VMFnArgs* args) {
1171 schoenebeck 3188 return VMChangeSynthParamFunction::execTemplate<
1172     &NoteBase::_Override::AmpLFODepth,
1173 schoenebeck 3193 Event::synth_param_amp_lfo_depth,
1174     true, 1000000, 0>( args, "change_amp_lfo_depth" );
1175 schoenebeck 3118 }
1176    
1177     // change_amp_lfo_freq() function
1178    
1179     VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_freq::exec(VMFnArgs* args) {
1180 schoenebeck 3188 return VMChangeSynthParamFunction::execTemplate<
1181     &NoteBase::_Override::AmpLFOFreq,
1182 schoenebeck 3193 Event::synth_param_amp_lfo_freq,
1183     true, 1000000, 0>( args, "change_amp_lfo_freq" );
1184 schoenebeck 3118 }
1185    
1186     // change_pitch_lfo_depth() function
1187    
1188     VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_depth::exec(VMFnArgs* args) {
1189 schoenebeck 3188 return VMChangeSynthParamFunction::execTemplate<
1190     &NoteBase::_Override::PitchLFODepth,
1191 schoenebeck 3193 Event::synth_param_pitch_lfo_depth,
1192     true, 1000000, 0>( args, "change_pitch_lfo_depth" );
1193 schoenebeck 3118 }
1194    
1195     // change_pitch_lfo_freq() function
1196    
1197     VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_freq::exec(VMFnArgs* args) {
1198 schoenebeck 3188 return VMChangeSynthParamFunction::execTemplate<
1199     &NoteBase::_Override::PitchLFOFreq,
1200 schoenebeck 3193 Event::synth_param_pitch_lfo_freq,
1201     true, 1000000, 0>( args, "change_pitch_lfo_freq" );
1202 schoenebeck 3118 }
1203    
1204 schoenebeck 3188 // change_vol_time() function
1205    
1206     VMFnResult* InstrumentScriptVMFunction_change_vol_time::exec(VMFnArgs* args) {
1207     return VMChangeSynthParamFunction::execTemplate<
1208     &NoteBase::_Override::VolumeTime,
1209 schoenebeck 3193 Event::synth_param_volume_time,
1210     false, NO_LIMIT, 0>( args, "change_vol_time" );
1211 schoenebeck 3188 }
1212    
1213     // change_tune_time() function
1214    
1215     VMFnResult* InstrumentScriptVMFunction_change_tune_time::exec(VMFnArgs* args) {
1216     return VMChangeSynthParamFunction::execTemplate<
1217     &NoteBase::_Override::PitchTime,
1218 schoenebeck 3193 Event::synth_param_pitch_time,
1219     false, NO_LIMIT, 0>( args, "change_tune_time" );
1220 schoenebeck 3188 }
1221    
1222 schoenebeck 3246 // template for change_*_curve() functions
1223    
1224     bool VMChangeFadeCurveFunction::acceptsArgType(int iArg, ExprType_t type) const {
1225     if (iArg == 0)
1226     return type == INT_EXPR || type == INT_ARR_EXPR;
1227     else
1228     return type == INT_EXPR;
1229     }
1230    
1231     template<fade_curve_t NoteBase::_Override::*T_noteParam, int T_synthParam>
1232     VMFnResult* VMChangeFadeCurveFunction::execTemplate(VMFnArgs* args, const char* functionName) {
1233     int value = args->arg(1)->asInt()->evalInt();
1234     switch (value) {
1235     case FADE_CURVE_LINEAR:
1236     case FADE_CURVE_EASE_IN_EASE_OUT:
1237     break;
1238     default:
1239     wrnMsg(String(functionName) + "(): invalid curve type passed as argument 2");
1240     return successResult();
1241     }
1242    
1243     AbstractEngineChannel* pEngineChannel =
1244     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1245    
1246     if (args->arg(0)->exprType() == INT_EXPR) {
1247     const ScriptID id = args->arg(0)->asInt()->evalInt();
1248     if (!id) {
1249     wrnMsg(String(functionName) + "(): note ID for argument 1 may not be zero");
1250     return successResult();
1251     }
1252     if (!id.isNoteID()) {
1253     wrnMsg(String(functionName) + "(): argument 1 is not a note ID");
1254     return successResult();
1255     }
1256    
1257     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1258     if (!pNote) return successResult();
1259    
1260     // if this change_*_curve() script function was called immediately after
1261     // note was triggered then immediately apply the synth parameter
1262     // change to Note object
1263     if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1264     pNote->Override.*T_noteParam = (fade_curve_t) value;
1265     } else { // otherwise schedule this synth parameter change ...
1266     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1267     e.Init(); // clear IDs
1268     e.Type = Event::type_note_synth_param;
1269     e.Param.NoteSynthParam.NoteID = id.noteID();
1270     e.Param.NoteSynthParam.Type = (Event::synth_param_t) T_synthParam;
1271     e.Param.NoteSynthParam.Delta = value;
1272     e.Param.NoteSynthParam.Relative = false;
1273    
1274     pEngineChannel->ScheduleEventMicroSec(&e, 0);
1275     }
1276     } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1277     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1278     for (int i = 0; i < ids->arraySize(); ++i) {
1279     const ScriptID id = ids->evalIntElement(i);
1280     if (!id || !id.isNoteID()) continue;
1281    
1282     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1283     if (!pNote) continue;
1284    
1285     // if this change_*_curve() script function was called immediately after
1286     // note was triggered then immediately apply the synth parameter
1287     // change to Note object
1288     if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1289     pNote->Override.*T_noteParam = (fade_curve_t) value;
1290     } else { // otherwise schedule this synth parameter change ...
1291     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1292     e.Init(); // clear IDs
1293     e.Type = Event::type_note_synth_param;
1294     e.Param.NoteSynthParam.NoteID = id.noteID();
1295     e.Param.NoteSynthParam.Type = (Event::synth_param_t) T_synthParam;
1296     e.Param.NoteSynthParam.Delta = value;
1297     e.Param.NoteSynthParam.Relative = false;
1298    
1299     pEngineChannel->ScheduleEventMicroSec(&e, 0);
1300     }
1301     }
1302     }
1303    
1304     return successResult();
1305     }
1306    
1307     // change_vol_curve() function
1308    
1309     VMFnResult* InstrumentScriptVMFunction_change_vol_curve::exec(VMFnArgs* args) {
1310     return VMChangeFadeCurveFunction::execTemplate<
1311     &NoteBase::_Override::VolumeCurve,
1312     Event::synth_param_volume_curve>( args, "change_vol_curve" );
1313     }
1314    
1315     // change_tune_curve() function
1316    
1317     VMFnResult* InstrumentScriptVMFunction_change_tune_curve::exec(VMFnArgs* args) {
1318     return VMChangeFadeCurveFunction::execTemplate<
1319     &NoteBase::_Override::PitchCurve,
1320     Event::synth_param_pitch_curve>( args, "change_tune_curve" );
1321     }
1322    
1323 schoenebeck 3188 // fade_in() function
1324    
1325     InstrumentScriptVMFunction_fade_in::InstrumentScriptVMFunction_fade_in(InstrumentScriptVM* parent)
1326     : m_vm(parent)
1327     {
1328     }
1329    
1330     bool InstrumentScriptVMFunction_fade_in::acceptsArgType(int iArg, ExprType_t type) const {
1331     if (iArg == 0)
1332     return type == INT_EXPR || type == INT_ARR_EXPR;
1333     else
1334     return type == INT_EXPR;
1335     }
1336    
1337     VMFnResult* InstrumentScriptVMFunction_fade_in::exec(VMFnArgs* args) {
1338     int duration = args->arg(1)->asInt()->evalInt();
1339     if (duration < 0) {
1340     wrnMsg("fade_in(): argument 2 may not be negative");
1341     duration = 0;
1342     }
1343     const float fDuration = float(duration) / 1000000.f; // convert microseconds to seconds
1344    
1345     AbstractEngineChannel* pEngineChannel =
1346     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1347    
1348     if (args->arg(0)->exprType() == INT_EXPR) {
1349     const ScriptID id = args->arg(0)->asInt()->evalInt();
1350     if (!id) {
1351     wrnMsg("fade_in(): note ID for argument 1 may not be zero");
1352     return successResult();
1353     }
1354     if (!id.isNoteID()) {
1355     wrnMsg("fade_in(): argument 1 is not a note ID");
1356     return successResult();
1357     }
1358    
1359     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1360     if (!pNote) return successResult();
1361    
1362     // if fade_in() was called immediately after note was triggered
1363     // then immediately apply a start volume of zero to Note object,
1364     // as well as the fade in duration
1365 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1366 schoenebeck 3188 pNote->Override.Volume = 0.f;
1367     pNote->Override.VolumeTime = fDuration;
1368     } else { // otherwise schedule a "volume time" change with the requested fade in duration ...
1369     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1370     e.Init(); // clear IDs
1371     e.Type = Event::type_note_synth_param;
1372     e.Param.NoteSynthParam.NoteID = id.noteID();
1373     e.Param.NoteSynthParam.Type = Event::synth_param_volume_time;
1374     e.Param.NoteSynthParam.Delta = fDuration;
1375     e.Param.NoteSynthParam.Relative = false;
1376    
1377     pEngineChannel->ScheduleEventMicroSec(&e, 0);
1378     }
1379     // and finally schedule a "volume" change, simply one time slice
1380     // ahead, with the final fade in volume (1.0)
1381     {
1382     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1383     e.Init(); // clear IDs
1384     e.Type = Event::type_note_synth_param;
1385     e.Param.NoteSynthParam.NoteID = id.noteID();
1386     e.Param.NoteSynthParam.Type = Event::synth_param_volume;
1387     e.Param.NoteSynthParam.Delta = 1.f;
1388     e.Param.NoteSynthParam.Relative = false;
1389    
1390     // scheduling with 0 delay would also work here, but +1 is more
1391     // safe regarding potential future implementation changes of the
1392     // scheduler (see API comments of RTAVLTree::insert())
1393     pEngineChannel->ScheduleEventMicroSec(&e, 1);
1394     }
1395     } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1396     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1397     for (int i = 0; i < ids->arraySize(); ++i) {
1398     const ScriptID id = ids->evalIntElement(i);
1399     if (!id || !id.isNoteID()) continue;
1400    
1401     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1402     if (!pNote) continue;
1403    
1404     // if fade_in() was called immediately after note was triggered
1405     // then immediately apply a start volume of zero to Note object,
1406     // as well as the fade in duration
1407 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1408 schoenebeck 3188 pNote->Override.Volume = 0.f;
1409     pNote->Override.VolumeTime = fDuration;
1410     } else { // otherwise schedule a "volume time" change with the requested fade in duration ...
1411     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1412     e.Init(); // clear IDs
1413     e.Type = Event::type_note_synth_param;
1414     e.Param.NoteSynthParam.NoteID = id.noteID();
1415     e.Param.NoteSynthParam.Type = Event::synth_param_volume_time;
1416     e.Param.NoteSynthParam.Delta = fDuration;
1417     e.Param.NoteSynthParam.Relative = false;
1418    
1419     pEngineChannel->ScheduleEventMicroSec(&e, 0);
1420     }
1421     // and finally schedule a "volume" change, simply one time slice
1422     // ahead, with the final fade in volume (1.0)
1423     {
1424     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1425     e.Init(); // clear IDs
1426     e.Type = Event::type_note_synth_param;
1427     e.Param.NoteSynthParam.NoteID = id.noteID();
1428     e.Param.NoteSynthParam.Type = Event::synth_param_volume;
1429     e.Param.NoteSynthParam.Delta = 1.f;
1430     e.Param.NoteSynthParam.Relative = false;
1431    
1432     // scheduling with 0 delay would also work here, but +1 is more
1433     // safe regarding potential future implementation changes of the
1434     // scheduler (see API comments of RTAVLTree::insert())
1435     pEngineChannel->ScheduleEventMicroSec(&e, 1);
1436     }
1437     }
1438     }
1439    
1440     return successResult();
1441     }
1442    
1443     // fade_out() function
1444    
1445     InstrumentScriptVMFunction_fade_out::InstrumentScriptVMFunction_fade_out(InstrumentScriptVM* parent)
1446     : m_vm(parent)
1447     {
1448     }
1449    
1450     bool InstrumentScriptVMFunction_fade_out::acceptsArgType(int iArg, ExprType_t type) const {
1451     if (iArg == 0)
1452     return type == INT_EXPR || type == INT_ARR_EXPR;
1453     else
1454     return type == INT_EXPR;
1455     }
1456    
1457     VMFnResult* InstrumentScriptVMFunction_fade_out::exec(VMFnArgs* args) {
1458     int duration = args->arg(1)->asInt()->evalInt();
1459     if (duration < 0) {
1460     wrnMsg("fade_out(): argument 2 may not be negative");
1461     duration = 0;
1462     }
1463     const float fDuration = float(duration) / 1000000.f; // convert microseconds to seconds
1464    
1465     bool stop = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : true;
1466    
1467     AbstractEngineChannel* pEngineChannel =
1468     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1469    
1470     if (args->arg(0)->exprType() == INT_EXPR) {
1471     const ScriptID id = args->arg(0)->asInt()->evalInt();
1472     if (!id) {
1473     wrnMsg("fade_out(): note ID for argument 1 may not be zero");
1474     return successResult();
1475     }
1476     if (!id.isNoteID()) {
1477     wrnMsg("fade_out(): argument 1 is not a note ID");
1478     return successResult();
1479     }
1480    
1481     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1482     if (!pNote) return successResult();
1483    
1484     // if fade_out() was called immediately after note was triggered
1485     // then immediately apply fade out duration to Note object
1486 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1487 schoenebeck 3188 pNote->Override.VolumeTime = fDuration;
1488     } else { // otherwise schedule a "volume time" change with the requested fade out duration ...
1489     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1490     e.Init(); // clear IDs
1491     e.Type = Event::type_note_synth_param;
1492     e.Param.NoteSynthParam.NoteID = id.noteID();
1493     e.Param.NoteSynthParam.Type = Event::synth_param_volume_time;
1494     e.Param.NoteSynthParam.Delta = fDuration;
1495     e.Param.NoteSynthParam.Relative = false;
1496    
1497     pEngineChannel->ScheduleEventMicroSec(&e, 0);
1498     }
1499     // now schedule a "volume" change, simply one time slice ahead, with
1500     // the final fade out volume (0.0)
1501     {
1502     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1503     e.Init(); // clear IDs
1504     e.Type = Event::type_note_synth_param;
1505     e.Param.NoteSynthParam.NoteID = id.noteID();
1506     e.Param.NoteSynthParam.Type = Event::synth_param_volume;
1507     e.Param.NoteSynthParam.Delta = 0.f;
1508     e.Param.NoteSynthParam.Relative = false;
1509    
1510     // scheduling with 0 delay would also work here, but +1 is more
1511     // safe regarding potential future implementation changes of the
1512     // scheduler (see API comments of RTAVLTree::insert())
1513     pEngineChannel->ScheduleEventMicroSec(&e, 1);
1514     }
1515     // and finally if stopping the note was requested after the fade out
1516     // completed, then schedule to kill the voice after the requested
1517     // duration
1518     if (stop) {
1519     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1520     e.Init(); // clear IDs
1521     e.Type = Event::type_kill_note;
1522     e.Param.Note.ID = id.noteID();
1523     e.Param.Note.Key = pNote->hostKey;
1524    
1525     pEngineChannel->ScheduleEventMicroSec(&e, duration + 1);
1526     }
1527     } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1528     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1529     for (int i = 0; i < ids->arraySize(); ++i) {
1530     const ScriptID id = ids->evalIntElement(i);
1531     if (!id || !id.isNoteID()) continue;
1532    
1533     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1534     if (!pNote) continue;
1535    
1536     // if fade_out() was called immediately after note was triggered
1537     // then immediately apply fade out duration to Note object
1538 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1539 schoenebeck 3188 pNote->Override.VolumeTime = fDuration;
1540     } else { // otherwise schedule a "volume time" change with the requested fade out duration ...
1541     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1542     e.Init(); // clear IDs
1543     e.Type = Event::type_note_synth_param;
1544     e.Param.NoteSynthParam.NoteID = id.noteID();
1545     e.Param.NoteSynthParam.Type = Event::synth_param_volume_time;
1546     e.Param.NoteSynthParam.Delta = fDuration;
1547     e.Param.NoteSynthParam.Relative = false;
1548    
1549     pEngineChannel->ScheduleEventMicroSec(&e, 0);
1550     }
1551     // now schedule a "volume" change, simply one time slice ahead, with
1552     // the final fade out volume (0.0)
1553     {
1554     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1555     e.Init(); // clear IDs
1556     e.Type = Event::type_note_synth_param;
1557     e.Param.NoteSynthParam.NoteID = id.noteID();
1558     e.Param.NoteSynthParam.Type = Event::synth_param_volume;
1559     e.Param.NoteSynthParam.Delta = 0.f;
1560     e.Param.NoteSynthParam.Relative = false;
1561    
1562     // scheduling with 0 delay would also work here, but +1 is more
1563     // safe regarding potential future implementation changes of the
1564     // scheduler (see API comments of RTAVLTree::insert())
1565     pEngineChannel->ScheduleEventMicroSec(&e, 1);
1566     }
1567     // and finally if stopping the note was requested after the fade out
1568     // completed, then schedule to kill the voice after the requested
1569     // duration
1570     if (stop) {
1571     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1572     e.Init(); // clear IDs
1573     e.Type = Event::type_kill_note;
1574     e.Param.Note.ID = id.noteID();
1575     e.Param.Note.Key = pNote->hostKey;
1576    
1577     pEngineChannel->ScheduleEventMicroSec(&e, duration + 1);
1578     }
1579     }
1580     }
1581    
1582     return successResult();
1583     }
1584    
1585 schoenebeck 3193 // get_event_par() function
1586    
1587     InstrumentScriptVMFunction_get_event_par::InstrumentScriptVMFunction_get_event_par(InstrumentScriptVM* parent)
1588     : m_vm(parent)
1589     {
1590     }
1591    
1592     VMFnResult* InstrumentScriptVMFunction_get_event_par::exec(VMFnArgs* args) {
1593     AbstractEngineChannel* pEngineChannel =
1594     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1595    
1596     const ScriptID id = args->arg(0)->asInt()->evalInt();
1597     if (!id) {
1598     wrnMsg("get_event_par(): note ID for argument 1 may not be zero");
1599     return successResult(0);
1600     }
1601     if (!id.isNoteID()) {
1602     wrnMsg("get_event_par(): argument 1 is not a note ID");
1603     return successResult(0);
1604     }
1605    
1606     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1607     if (!pNote) {
1608     wrnMsg("get_event_par(): no note alive with that note ID of argument 1");
1609     return successResult(0);
1610     }
1611    
1612     const int parameter = args->arg(1)->asInt()->evalInt();
1613     switch (parameter) {
1614     case EVENT_PAR_NOTE:
1615     return successResult(pNote->cause.Param.Note.Key);
1616     case EVENT_PAR_VELOCITY:
1617     return successResult(pNote->cause.Param.Note.Velocity);
1618     case EVENT_PAR_VOLUME:
1619     return successResult(
1620     RTMath::LinRatioToDecibel(pNote->Override.Volume) * 1000.f
1621     );
1622     case EVENT_PAR_TUNE:
1623     return successResult(
1624     RTMath::FreqRatioToCents(pNote->Override.Pitch) * 1000.f
1625     );
1626     case EVENT_PAR_0:
1627     return successResult(pNote->userPar[0]);
1628     case EVENT_PAR_1:
1629     return successResult(pNote->userPar[1]);
1630     case EVENT_PAR_2:
1631     return successResult(pNote->userPar[2]);
1632     case EVENT_PAR_3:
1633     return successResult(pNote->userPar[3]);
1634     }
1635    
1636     wrnMsg("get_event_par(): argument 2 is an invalid event parameter");
1637     return successResult(0);
1638     }
1639    
1640     // set_event_par() function
1641    
1642     InstrumentScriptVMFunction_set_event_par::InstrumentScriptVMFunction_set_event_par(InstrumentScriptVM* parent)
1643     : m_vm(parent)
1644     {
1645     }
1646    
1647     VMFnResult* InstrumentScriptVMFunction_set_event_par::exec(VMFnArgs* args) {
1648     AbstractEngineChannel* pEngineChannel =
1649     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1650    
1651     const ScriptID id = args->arg(0)->asInt()->evalInt();
1652     if (!id) {
1653     wrnMsg("set_event_par(): note ID for argument 1 may not be zero");
1654     return successResult();
1655     }
1656     if (!id.isNoteID()) {
1657     wrnMsg("set_event_par(): argument 1 is not a note ID");
1658     return successResult();
1659     }
1660    
1661     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1662     if (!pNote) return successResult();
1663    
1664     const int parameter = args->arg(1)->asInt()->evalInt();
1665     const int value = args->arg(2)->asInt()->evalInt();
1666    
1667     switch (parameter) {
1668     case EVENT_PAR_NOTE:
1669     if (value < 0 || value > 127) {
1670     wrnMsg("set_event_par(): note number of argument 3 is out of range");
1671     return successResult();
1672     }
1673 schoenebeck 3214 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1674 schoenebeck 3193 pNote->cause.Param.Note.Key = value;
1675 schoenebeck 3214 m_vm->m_event->cause.Param.Note.Key = value;
1676     } else {
1677 schoenebeck 3193 wrnMsg("set_event_par(): note number can only be changed when note is new");
1678 schoenebeck 3214 }
1679 schoenebeck 3193 return successResult();
1680     case EVENT_PAR_VELOCITY:
1681     if (value < 0 || value > 127) {
1682     wrnMsg("set_event_par(): velocity 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.Velocity = value;
1687 schoenebeck 3214 m_vm->m_event->cause.Param.Note.Velocity = value;
1688     } else {
1689 schoenebeck 3193 wrnMsg("set_event_par(): velocity can only be changed when note is new");
1690 schoenebeck 3214 }
1691 schoenebeck 3193 return successResult();
1692     case EVENT_PAR_VOLUME:
1693     wrnMsg("set_event_par(): changing volume by this function is currently not supported, use change_vol() instead");
1694     return successResult();
1695     case EVENT_PAR_TUNE:
1696     wrnMsg("set_event_par(): changing tune by this function is currently not supported, use change_tune() instead");
1697     return successResult();
1698     case EVENT_PAR_0:
1699     pNote->userPar[0] = value;
1700     return successResult();
1701     case EVENT_PAR_1:
1702     pNote->userPar[1] = value;
1703     return successResult();
1704     case EVENT_PAR_2:
1705     pNote->userPar[2] = value;
1706     return successResult();
1707     case EVENT_PAR_3:
1708     pNote->userPar[3] = value;
1709     return successResult();
1710     }
1711    
1712     wrnMsg("set_event_par(): argument 2 is an invalid event parameter");
1713     return successResult();
1714     }
1715    
1716 schoenebeck 3214 // change_note() function
1717    
1718     InstrumentScriptVMFunction_change_note::InstrumentScriptVMFunction_change_note(InstrumentScriptVM* parent)
1719     : m_vm(parent)
1720     {
1721     }
1722    
1723     VMFnResult* InstrumentScriptVMFunction_change_note::exec(VMFnArgs* args) {
1724     AbstractEngineChannel* pEngineChannel =
1725     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1726    
1727     const ScriptID id = args->arg(0)->asInt()->evalInt();
1728     if (!id) {
1729     wrnMsg("change_note(): note ID for argument 1 may not be zero");
1730     return successResult();
1731     }
1732     if (!id.isNoteID()) {
1733     wrnMsg("change_note(): argument 1 is not a note ID");
1734     return successResult();
1735     }
1736    
1737     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1738     if (!pNote) return successResult();
1739    
1740     const int value = args->arg(1)->asInt()->evalInt();
1741     if (value < 0 || value > 127) {
1742     wrnMsg("change_note(): note number of argument 2 is out of range");
1743     return successResult();
1744     }
1745    
1746     if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1747     pNote->cause.Param.Note.Key = value;
1748     m_vm->m_event->cause.Param.Note.Key = value;
1749 schoenebeck 3216 } else {
1750 schoenebeck 3214 wrnMsg("change_note(): note number can only be changed when note is new");
1751     }
1752    
1753     return successResult();
1754     }
1755    
1756     // change_velo() function
1757    
1758     InstrumentScriptVMFunction_change_velo::InstrumentScriptVMFunction_change_velo(InstrumentScriptVM* parent)
1759     : m_vm(parent)
1760     {
1761     }
1762    
1763     VMFnResult* InstrumentScriptVMFunction_change_velo::exec(VMFnArgs* args) {
1764     AbstractEngineChannel* pEngineChannel =
1765     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1766    
1767     const ScriptID id = args->arg(0)->asInt()->evalInt();
1768     if (!id) {
1769     wrnMsg("change_velo(): note ID for argument 1 may not be zero");
1770     return successResult();
1771     }
1772     if (!id.isNoteID()) {
1773     wrnMsg("change_velo(): argument 1 is not a note ID");
1774     return successResult();
1775     }
1776    
1777     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1778     if (!pNote) return successResult();
1779    
1780     const int value = args->arg(1)->asInt()->evalInt();
1781     if (value < 0 || value > 127) {
1782     wrnMsg("change_velo(): velocity of argument 2 is out of range");
1783     return successResult();
1784     }
1785    
1786     if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1787     pNote->cause.Param.Note.Velocity = value;
1788     m_vm->m_event->cause.Param.Note.Velocity = value;
1789 schoenebeck 3216 } else {
1790 schoenebeck 3214 wrnMsg("change_velo(): velocity can only be changed when note is new");
1791     }
1792    
1793     return successResult();
1794     }
1795    
1796 schoenebeck 2935 // event_status() function
1797    
1798     InstrumentScriptVMFunction_event_status::InstrumentScriptVMFunction_event_status(InstrumentScriptVM* parent)
1799     : m_vm(parent)
1800     {
1801     }
1802    
1803     VMFnResult* InstrumentScriptVMFunction_event_status::exec(VMFnArgs* args) {
1804     AbstractEngineChannel* pEngineChannel =
1805     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1806    
1807     const ScriptID id = args->arg(0)->asInt()->evalInt();
1808     if (!id) {
1809     wrnMsg("event_status(): note ID for argument 1 may not be zero");
1810     return successResult(EVENT_STATUS_INACTIVE);
1811     }
1812     if (!id.isNoteID()) {
1813     wrnMsg("event_status(): argument 1 is not a note ID");
1814     return successResult(EVENT_STATUS_INACTIVE);
1815     }
1816    
1817     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1818     return successResult(pNote ? EVENT_STATUS_NOTE_QUEUE : EVENT_STATUS_INACTIVE);
1819     }
1820    
1821 schoenebeck 2948 // wait() function (overrides core wait() implementation)
1822    
1823     InstrumentScriptVMFunction_wait::InstrumentScriptVMFunction_wait(InstrumentScriptVM* parent)
1824     : CoreVMFunction_wait(parent)
1825     {
1826     }
1827    
1828     VMFnResult* InstrumentScriptVMFunction_wait::exec(VMFnArgs* args) {
1829     InstrumentScriptVM* m_vm = (InstrumentScriptVM*) vm;
1830    
1831     // this might be set by passing 1 with the 2nd argument of built-in stop_wait() function
1832     if (m_vm->m_event->ignoreAllWaitCalls) return successResult();
1833    
1834     return CoreVMFunction_wait::exec(args);
1835     }
1836    
1837     // stop_wait() function
1838    
1839     InstrumentScriptVMFunction_stop_wait::InstrumentScriptVMFunction_stop_wait(InstrumentScriptVM* parent)
1840     : m_vm(parent)
1841     {
1842     }
1843    
1844     VMFnResult* InstrumentScriptVMFunction_stop_wait::exec(VMFnArgs* args) {
1845     AbstractEngineChannel* pEngineChannel =
1846     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1847    
1848     const script_callback_id_t id = args->arg(0)->asInt()->evalInt();
1849     if (!id) {
1850     wrnMsg("stop_wait(): callback ID for argument 1 may not be zero");
1851     return successResult();
1852     }
1853    
1854     RTList<ScriptEvent>::Iterator itCallback = pEngineChannel->ScriptCallbackByID(id);
1855     if (!itCallback) return successResult(); // ignore if callback is i.e. not alive anymore
1856    
1857     const bool disableWaitForever =
1858     (args->argsCount() >= 2) ? (args->arg(1)->asInt()->evalInt() == 1) : false;
1859    
1860     pEngineChannel->ScheduleResumeOfScriptCallback(
1861 schoenebeck 3205 itCallback, m_vm->m_event->scheduleTime, disableWaitForever
1862 schoenebeck 2948 );
1863    
1864     return successResult();
1865     }
1866    
1867 schoenebeck 2596 } // namespace LinuxSampler

  ViewVC Help
Powered by ViewVC