/[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 3218 - (hide annotations) (download)
Thu May 25 16:32:17 2017 UTC (6 years, 11 months ago) by schoenebeck
File size: 76396 byte(s)
* NKSP: Adjusted behavior of "change_vol()" and "change_tune()" to a more
  intuitive behavior if used in combination with "change_vol_time()" or
  "change_tune_time()" respectively: now tuning/volume changes are only
  assigned (without delay) immediately to a new note if the respective
  timing function has not been called before, otherwise the volume/tuning
  changes are automatically faded (before, only the event's time stamp was
  relevant).
* Bumped version (2.0.0.svn50).

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 3118 bool VMChangeSynthParamFunction::acceptsArgType(int iArg, ExprType_t type) const {
1075     if (iArg == 0)
1076     return type == INT_EXPR || type == INT_ARR_EXPR;
1077     else
1078 schoenebeck 3188 return type == INT_EXPR;
1079 schoenebeck 3118 }
1080    
1081 schoenebeck 3193 // Arbitrarily chosen constant value symbolizing "no limit".
1082     #define NO_LIMIT 1315916909
1083    
1084 schoenebeck 3188 template<float NoteBase::_Override::*T_noteParam, int T_synthParam,
1085     bool T_isNormalizedParam, int T_maxValue, int T_minValue>
1086 schoenebeck 3118 VMFnResult* VMChangeSynthParamFunction::execTemplate(VMFnArgs* args, const char* functionName) {
1087     int value = args->arg(1)->asInt()->evalInt();
1088 schoenebeck 3193 if (T_maxValue != NO_LIMIT && value > T_maxValue) {
1089 schoenebeck 3188 wrnMsg(String(functionName) + "(): argument 2 may not be larger than " + ToString(T_maxValue));
1090     value = T_maxValue;
1091 schoenebeck 3193 } else if (T_minValue != NO_LIMIT && value < T_minValue) {
1092 schoenebeck 3188 if (T_minValue == 0)
1093     wrnMsg(String(functionName) + "(): argument 2 may not be negative");
1094     else
1095     wrnMsg(String(functionName) + "(): argument 2 may not be smaller than " + ToString(T_minValue));
1096     value = T_minValue;
1097 schoenebeck 3118 }
1098 schoenebeck 3188 const float fValue = (T_isNormalizedParam) ?
1099     float(value) / float(T_maxValue) : // convert to 0.0 .. 1.0 value range
1100     float(value) / 1000000.f; // assuming microseconds here, convert to seconds
1101 schoenebeck 3118
1102     AbstractEngineChannel* pEngineChannel =
1103     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1104    
1105     if (args->arg(0)->exprType() == INT_EXPR) {
1106     const ScriptID id = args->arg(0)->asInt()->evalInt();
1107     if (!id) {
1108     wrnMsg(String(functionName) + "(): note ID for argument 1 may not be zero");
1109     return successResult();
1110     }
1111     if (!id.isNoteID()) {
1112     wrnMsg(String(functionName) + "(): argument 1 is not a note ID");
1113     return successResult();
1114     }
1115    
1116     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1117     if (!pNote) return successResult();
1118    
1119     // if this change_*() script function was called immediately after
1120     // note was triggered then immediately apply the synth parameter
1121     // change to Note object
1122 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1123 schoenebeck 3118 pNote->Override.*T_noteParam = fValue;
1124     } else { // otherwise schedule this synth parameter change ...
1125     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1126     e.Init(); // clear IDs
1127     e.Type = Event::type_note_synth_param;
1128     e.Param.NoteSynthParam.NoteID = id.noteID();
1129     e.Param.NoteSynthParam.Type = (Event::synth_param_t) T_synthParam;
1130     e.Param.NoteSynthParam.Delta = fValue;
1131     e.Param.NoteSynthParam.Relative = false;
1132    
1133     pEngineChannel->ScheduleEventMicroSec(&e, 0);
1134     }
1135     } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1136     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1137     for (int i = 0; i < ids->arraySize(); ++i) {
1138     const ScriptID id = ids->evalIntElement(i);
1139     if (!id || !id.isNoteID()) continue;
1140    
1141     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1142     if (!pNote) continue;
1143    
1144     // if this change_*() script function was called immediately after
1145     // note was triggered then immediately apply the synth parameter
1146     // change to Note object
1147 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1148 schoenebeck 3118 pNote->Override.*T_noteParam = fValue;
1149     } else { // otherwise schedule this synth parameter change ...
1150     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1151     e.Init(); // clear IDs
1152     e.Type = Event::type_note_synth_param;
1153     e.Param.NoteSynthParam.NoteID = id.noteID();
1154     e.Param.NoteSynthParam.Type = (Event::synth_param_t) T_synthParam;
1155     e.Param.NoteSynthParam.Delta = fValue;
1156     e.Param.NoteSynthParam.Relative = false;
1157    
1158     pEngineChannel->ScheduleEventMicroSec(&e, 0);
1159     }
1160     }
1161     }
1162    
1163     return successResult();
1164     }
1165    
1166     // change_amp_lfo_depth() function
1167    
1168     VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_depth::exec(VMFnArgs* args) {
1169 schoenebeck 3188 return VMChangeSynthParamFunction::execTemplate<
1170     &NoteBase::_Override::AmpLFODepth,
1171 schoenebeck 3193 Event::synth_param_amp_lfo_depth,
1172     true, 1000000, 0>( args, "change_amp_lfo_depth" );
1173 schoenebeck 3118 }
1174    
1175     // change_amp_lfo_freq() function
1176    
1177     VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_freq::exec(VMFnArgs* args) {
1178 schoenebeck 3188 return VMChangeSynthParamFunction::execTemplate<
1179     &NoteBase::_Override::AmpLFOFreq,
1180 schoenebeck 3193 Event::synth_param_amp_lfo_freq,
1181     true, 1000000, 0>( args, "change_amp_lfo_freq" );
1182 schoenebeck 3118 }
1183    
1184     // change_pitch_lfo_depth() function
1185    
1186     VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_depth::exec(VMFnArgs* args) {
1187 schoenebeck 3188 return VMChangeSynthParamFunction::execTemplate<
1188     &NoteBase::_Override::PitchLFODepth,
1189 schoenebeck 3193 Event::synth_param_pitch_lfo_depth,
1190     true, 1000000, 0>( args, "change_pitch_lfo_depth" );
1191 schoenebeck 3118 }
1192    
1193     // change_pitch_lfo_freq() function
1194    
1195     VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_freq::exec(VMFnArgs* args) {
1196 schoenebeck 3188 return VMChangeSynthParamFunction::execTemplate<
1197     &NoteBase::_Override::PitchLFOFreq,
1198 schoenebeck 3193 Event::synth_param_pitch_lfo_freq,
1199     true, 1000000, 0>( args, "change_pitch_lfo_freq" );
1200 schoenebeck 3118 }
1201    
1202 schoenebeck 3188 // change_vol_time() function
1203    
1204     VMFnResult* InstrumentScriptVMFunction_change_vol_time::exec(VMFnArgs* args) {
1205     return VMChangeSynthParamFunction::execTemplate<
1206     &NoteBase::_Override::VolumeTime,
1207 schoenebeck 3193 Event::synth_param_volume_time,
1208     false, NO_LIMIT, 0>( args, "change_vol_time" );
1209 schoenebeck 3188 }
1210    
1211     // change_tune_time() function
1212    
1213     VMFnResult* InstrumentScriptVMFunction_change_tune_time::exec(VMFnArgs* args) {
1214     return VMChangeSynthParamFunction::execTemplate<
1215     &NoteBase::_Override::PitchTime,
1216 schoenebeck 3193 Event::synth_param_pitch_time,
1217     false, NO_LIMIT, 0>( args, "change_tune_time" );
1218 schoenebeck 3188 }
1219    
1220     // fade_in() function
1221    
1222     InstrumentScriptVMFunction_fade_in::InstrumentScriptVMFunction_fade_in(InstrumentScriptVM* parent)
1223     : m_vm(parent)
1224     {
1225     }
1226    
1227     bool InstrumentScriptVMFunction_fade_in::acceptsArgType(int iArg, ExprType_t type) const {
1228     if (iArg == 0)
1229     return type == INT_EXPR || type == INT_ARR_EXPR;
1230     else
1231     return type == INT_EXPR;
1232     }
1233    
1234     VMFnResult* InstrumentScriptVMFunction_fade_in::exec(VMFnArgs* args) {
1235     int duration = args->arg(1)->asInt()->evalInt();
1236     if (duration < 0) {
1237     wrnMsg("fade_in(): argument 2 may not be negative");
1238     duration = 0;
1239     }
1240     const float fDuration = float(duration) / 1000000.f; // convert microseconds to seconds
1241    
1242     AbstractEngineChannel* pEngineChannel =
1243     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1244    
1245     if (args->arg(0)->exprType() == INT_EXPR) {
1246     const ScriptID id = args->arg(0)->asInt()->evalInt();
1247     if (!id) {
1248     wrnMsg("fade_in(): note ID for argument 1 may not be zero");
1249     return successResult();
1250     }
1251     if (!id.isNoteID()) {
1252     wrnMsg("fade_in(): argument 1 is not a note ID");
1253     return successResult();
1254     }
1255    
1256     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1257     if (!pNote) return successResult();
1258    
1259     // if fade_in() was called immediately after note was triggered
1260     // then immediately apply a start volume of zero to Note object,
1261     // as well as the fade in duration
1262 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1263 schoenebeck 3188 pNote->Override.Volume = 0.f;
1264     pNote->Override.VolumeTime = fDuration;
1265     } else { // otherwise schedule a "volume time" change with the requested fade in duration ...
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_volume_time;
1271     e.Param.NoteSynthParam.Delta = fDuration;
1272     e.Param.NoteSynthParam.Relative = false;
1273    
1274     pEngineChannel->ScheduleEventMicroSec(&e, 0);
1275     }
1276     // and finally schedule a "volume" change, simply one time slice
1277     // ahead, with the final fade in volume (1.0)
1278     {
1279     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1280     e.Init(); // clear IDs
1281     e.Type = Event::type_note_synth_param;
1282     e.Param.NoteSynthParam.NoteID = id.noteID();
1283     e.Param.NoteSynthParam.Type = Event::synth_param_volume;
1284     e.Param.NoteSynthParam.Delta = 1.f;
1285     e.Param.NoteSynthParam.Relative = false;
1286    
1287     // scheduling with 0 delay would also work here, but +1 is more
1288     // safe regarding potential future implementation changes of the
1289     // scheduler (see API comments of RTAVLTree::insert())
1290     pEngineChannel->ScheduleEventMicroSec(&e, 1);
1291     }
1292     } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1293     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1294     for (int i = 0; i < ids->arraySize(); ++i) {
1295     const ScriptID id = ids->evalIntElement(i);
1296     if (!id || !id.isNoteID()) continue;
1297    
1298     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1299     if (!pNote) continue;
1300    
1301     // if fade_in() was called immediately after note was triggered
1302     // then immediately apply a start volume of zero to Note object,
1303     // as well as the fade in duration
1304 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1305 schoenebeck 3188 pNote->Override.Volume = 0.f;
1306     pNote->Override.VolumeTime = fDuration;
1307     } else { // otherwise schedule a "volume time" change with the requested fade in duration ...
1308     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1309     e.Init(); // clear IDs
1310     e.Type = Event::type_note_synth_param;
1311     e.Param.NoteSynthParam.NoteID = id.noteID();
1312     e.Param.NoteSynthParam.Type = Event::synth_param_volume_time;
1313     e.Param.NoteSynthParam.Delta = fDuration;
1314     e.Param.NoteSynthParam.Relative = false;
1315    
1316     pEngineChannel->ScheduleEventMicroSec(&e, 0);
1317     }
1318     // and finally schedule a "volume" change, simply one time slice
1319     // ahead, with the final fade in volume (1.0)
1320     {
1321     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1322     e.Init(); // clear IDs
1323     e.Type = Event::type_note_synth_param;
1324     e.Param.NoteSynthParam.NoteID = id.noteID();
1325     e.Param.NoteSynthParam.Type = Event::synth_param_volume;
1326     e.Param.NoteSynthParam.Delta = 1.f;
1327     e.Param.NoteSynthParam.Relative = false;
1328    
1329     // scheduling with 0 delay would also work here, but +1 is more
1330     // safe regarding potential future implementation changes of the
1331     // scheduler (see API comments of RTAVLTree::insert())
1332     pEngineChannel->ScheduleEventMicroSec(&e, 1);
1333     }
1334     }
1335     }
1336    
1337     return successResult();
1338     }
1339    
1340     // fade_out() function
1341    
1342     InstrumentScriptVMFunction_fade_out::InstrumentScriptVMFunction_fade_out(InstrumentScriptVM* parent)
1343     : m_vm(parent)
1344     {
1345     }
1346    
1347     bool InstrumentScriptVMFunction_fade_out::acceptsArgType(int iArg, ExprType_t type) const {
1348     if (iArg == 0)
1349     return type == INT_EXPR || type == INT_ARR_EXPR;
1350     else
1351     return type == INT_EXPR;
1352     }
1353    
1354     VMFnResult* InstrumentScriptVMFunction_fade_out::exec(VMFnArgs* args) {
1355     int duration = args->arg(1)->asInt()->evalInt();
1356     if (duration < 0) {
1357     wrnMsg("fade_out(): argument 2 may not be negative");
1358     duration = 0;
1359     }
1360     const float fDuration = float(duration) / 1000000.f; // convert microseconds to seconds
1361    
1362     bool stop = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : true;
1363    
1364     AbstractEngineChannel* pEngineChannel =
1365     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1366    
1367     if (args->arg(0)->exprType() == INT_EXPR) {
1368     const ScriptID id = args->arg(0)->asInt()->evalInt();
1369     if (!id) {
1370     wrnMsg("fade_out(): note ID for argument 1 may not be zero");
1371     return successResult();
1372     }
1373     if (!id.isNoteID()) {
1374     wrnMsg("fade_out(): argument 1 is not a note ID");
1375     return successResult();
1376     }
1377    
1378     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1379     if (!pNote) return successResult();
1380    
1381     // if fade_out() was called immediately after note was triggered
1382     // then immediately apply fade out duration to Note object
1383 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1384 schoenebeck 3188 pNote->Override.VolumeTime = fDuration;
1385     } else { // otherwise schedule a "volume time" change with the requested fade out duration ...
1386     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1387     e.Init(); // clear IDs
1388     e.Type = Event::type_note_synth_param;
1389     e.Param.NoteSynthParam.NoteID = id.noteID();
1390     e.Param.NoteSynthParam.Type = Event::synth_param_volume_time;
1391     e.Param.NoteSynthParam.Delta = fDuration;
1392     e.Param.NoteSynthParam.Relative = false;
1393    
1394     pEngineChannel->ScheduleEventMicroSec(&e, 0);
1395     }
1396     // now schedule a "volume" change, simply one time slice ahead, with
1397     // the final fade out volume (0.0)
1398     {
1399     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1400     e.Init(); // clear IDs
1401     e.Type = Event::type_note_synth_param;
1402     e.Param.NoteSynthParam.NoteID = id.noteID();
1403     e.Param.NoteSynthParam.Type = Event::synth_param_volume;
1404     e.Param.NoteSynthParam.Delta = 0.f;
1405     e.Param.NoteSynthParam.Relative = false;
1406    
1407     // scheduling with 0 delay would also work here, but +1 is more
1408     // safe regarding potential future implementation changes of the
1409     // scheduler (see API comments of RTAVLTree::insert())
1410     pEngineChannel->ScheduleEventMicroSec(&e, 1);
1411     }
1412     // and finally if stopping the note was requested after the fade out
1413     // completed, then schedule to kill the voice after the requested
1414     // duration
1415     if (stop) {
1416     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1417     e.Init(); // clear IDs
1418     e.Type = Event::type_kill_note;
1419     e.Param.Note.ID = id.noteID();
1420     e.Param.Note.Key = pNote->hostKey;
1421    
1422     pEngineChannel->ScheduleEventMicroSec(&e, duration + 1);
1423     }
1424     } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1425     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1426     for (int i = 0; i < ids->arraySize(); ++i) {
1427     const ScriptID id = ids->evalIntElement(i);
1428     if (!id || !id.isNoteID()) continue;
1429    
1430     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1431     if (!pNote) continue;
1432    
1433     // if fade_out() was called immediately after note was triggered
1434     // then immediately apply fade out duration to Note object
1435 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1436 schoenebeck 3188 pNote->Override.VolumeTime = fDuration;
1437     } else { // otherwise schedule a "volume time" change with the requested fade out duration ...
1438     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1439     e.Init(); // clear IDs
1440     e.Type = Event::type_note_synth_param;
1441     e.Param.NoteSynthParam.NoteID = id.noteID();
1442     e.Param.NoteSynthParam.Type = Event::synth_param_volume_time;
1443     e.Param.NoteSynthParam.Delta = fDuration;
1444     e.Param.NoteSynthParam.Relative = false;
1445    
1446     pEngineChannel->ScheduleEventMicroSec(&e, 0);
1447     }
1448     // now schedule a "volume" change, simply one time slice ahead, with
1449     // the final fade out volume (0.0)
1450     {
1451     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1452     e.Init(); // clear IDs
1453     e.Type = Event::type_note_synth_param;
1454     e.Param.NoteSynthParam.NoteID = id.noteID();
1455     e.Param.NoteSynthParam.Type = Event::synth_param_volume;
1456     e.Param.NoteSynthParam.Delta = 0.f;
1457     e.Param.NoteSynthParam.Relative = false;
1458    
1459     // scheduling with 0 delay would also work here, but +1 is more
1460     // safe regarding potential future implementation changes of the
1461     // scheduler (see API comments of RTAVLTree::insert())
1462     pEngineChannel->ScheduleEventMicroSec(&e, 1);
1463     }
1464     // and finally if stopping the note was requested after the fade out
1465     // completed, then schedule to kill the voice after the requested
1466     // duration
1467     if (stop) {
1468     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1469     e.Init(); // clear IDs
1470     e.Type = Event::type_kill_note;
1471     e.Param.Note.ID = id.noteID();
1472     e.Param.Note.Key = pNote->hostKey;
1473    
1474     pEngineChannel->ScheduleEventMicroSec(&e, duration + 1);
1475     }
1476     }
1477     }
1478    
1479     return successResult();
1480     }
1481    
1482 schoenebeck 3193 // get_event_par() function
1483    
1484     InstrumentScriptVMFunction_get_event_par::InstrumentScriptVMFunction_get_event_par(InstrumentScriptVM* parent)
1485     : m_vm(parent)
1486     {
1487     }
1488    
1489     VMFnResult* InstrumentScriptVMFunction_get_event_par::exec(VMFnArgs* args) {
1490     AbstractEngineChannel* pEngineChannel =
1491     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1492    
1493     const ScriptID id = args->arg(0)->asInt()->evalInt();
1494     if (!id) {
1495     wrnMsg("get_event_par(): note ID for argument 1 may not be zero");
1496     return successResult(0);
1497     }
1498     if (!id.isNoteID()) {
1499     wrnMsg("get_event_par(): argument 1 is not a note ID");
1500     return successResult(0);
1501     }
1502    
1503     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1504     if (!pNote) {
1505     wrnMsg("get_event_par(): no note alive with that note ID of argument 1");
1506     return successResult(0);
1507     }
1508    
1509     const int parameter = args->arg(1)->asInt()->evalInt();
1510     switch (parameter) {
1511     case EVENT_PAR_NOTE:
1512     return successResult(pNote->cause.Param.Note.Key);
1513     case EVENT_PAR_VELOCITY:
1514     return successResult(pNote->cause.Param.Note.Velocity);
1515     case EVENT_PAR_VOLUME:
1516     return successResult(
1517     RTMath::LinRatioToDecibel(pNote->Override.Volume) * 1000.f
1518     );
1519     case EVENT_PAR_TUNE:
1520     return successResult(
1521     RTMath::FreqRatioToCents(pNote->Override.Pitch) * 1000.f
1522     );
1523     case EVENT_PAR_0:
1524     return successResult(pNote->userPar[0]);
1525     case EVENT_PAR_1:
1526     return successResult(pNote->userPar[1]);
1527     case EVENT_PAR_2:
1528     return successResult(pNote->userPar[2]);
1529     case EVENT_PAR_3:
1530     return successResult(pNote->userPar[3]);
1531     }
1532    
1533     wrnMsg("get_event_par(): argument 2 is an invalid event parameter");
1534     return successResult(0);
1535     }
1536    
1537     // set_event_par() function
1538    
1539     InstrumentScriptVMFunction_set_event_par::InstrumentScriptVMFunction_set_event_par(InstrumentScriptVM* parent)
1540     : m_vm(parent)
1541     {
1542     }
1543    
1544     VMFnResult* InstrumentScriptVMFunction_set_event_par::exec(VMFnArgs* args) {
1545     AbstractEngineChannel* pEngineChannel =
1546     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1547    
1548     const ScriptID id = args->arg(0)->asInt()->evalInt();
1549     if (!id) {
1550     wrnMsg("set_event_par(): note ID for argument 1 may not be zero");
1551     return successResult();
1552     }
1553     if (!id.isNoteID()) {
1554     wrnMsg("set_event_par(): argument 1 is not a note ID");
1555     return successResult();
1556     }
1557    
1558     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1559     if (!pNote) return successResult();
1560    
1561     const int parameter = args->arg(1)->asInt()->evalInt();
1562     const int value = args->arg(2)->asInt()->evalInt();
1563    
1564     switch (parameter) {
1565     case EVENT_PAR_NOTE:
1566     if (value < 0 || value > 127) {
1567     wrnMsg("set_event_par(): note number 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.Key = value;
1572 schoenebeck 3214 m_vm->m_event->cause.Param.Note.Key = value;
1573     } else {
1574 schoenebeck 3193 wrnMsg("set_event_par(): note number can only be changed when note is new");
1575 schoenebeck 3214 }
1576 schoenebeck 3193 return successResult();
1577     case EVENT_PAR_VELOCITY:
1578     if (value < 0 || value > 127) {
1579     wrnMsg("set_event_par(): velocity of argument 3 is out of range");
1580     return successResult();
1581     }
1582 schoenebeck 3214 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1583 schoenebeck 3193 pNote->cause.Param.Note.Velocity = value;
1584 schoenebeck 3214 m_vm->m_event->cause.Param.Note.Velocity = value;
1585     } else {
1586 schoenebeck 3193 wrnMsg("set_event_par(): velocity can only be changed when note is new");
1587 schoenebeck 3214 }
1588 schoenebeck 3193 return successResult();
1589     case EVENT_PAR_VOLUME:
1590     wrnMsg("set_event_par(): changing volume by this function is currently not supported, use change_vol() instead");
1591     return successResult();
1592     case EVENT_PAR_TUNE:
1593     wrnMsg("set_event_par(): changing tune by this function is currently not supported, use change_tune() instead");
1594     return successResult();
1595     case EVENT_PAR_0:
1596     pNote->userPar[0] = value;
1597     return successResult();
1598     case EVENT_PAR_1:
1599     pNote->userPar[1] = value;
1600     return successResult();
1601     case EVENT_PAR_2:
1602     pNote->userPar[2] = value;
1603     return successResult();
1604     case EVENT_PAR_3:
1605     pNote->userPar[3] = value;
1606     return successResult();
1607     }
1608    
1609     wrnMsg("set_event_par(): argument 2 is an invalid event parameter");
1610     return successResult();
1611     }
1612    
1613 schoenebeck 3214 // change_note() function
1614    
1615     InstrumentScriptVMFunction_change_note::InstrumentScriptVMFunction_change_note(InstrumentScriptVM* parent)
1616     : m_vm(parent)
1617     {
1618     }
1619    
1620     VMFnResult* InstrumentScriptVMFunction_change_note::exec(VMFnArgs* args) {
1621     AbstractEngineChannel* pEngineChannel =
1622     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1623    
1624     const ScriptID id = args->arg(0)->asInt()->evalInt();
1625     if (!id) {
1626     wrnMsg("change_note(): note ID for argument 1 may not be zero");
1627     return successResult();
1628     }
1629     if (!id.isNoteID()) {
1630     wrnMsg("change_note(): argument 1 is not a note ID");
1631     return successResult();
1632     }
1633    
1634     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1635     if (!pNote) return successResult();
1636    
1637     const int value = args->arg(1)->asInt()->evalInt();
1638     if (value < 0 || value > 127) {
1639     wrnMsg("change_note(): note number of argument 2 is out of range");
1640     return successResult();
1641     }
1642    
1643     if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1644     pNote->cause.Param.Note.Key = value;
1645     m_vm->m_event->cause.Param.Note.Key = value;
1646 schoenebeck 3216 } else {
1647 schoenebeck 3214 wrnMsg("change_note(): note number can only be changed when note is new");
1648     }
1649    
1650     return successResult();
1651     }
1652    
1653     // change_velo() function
1654    
1655     InstrumentScriptVMFunction_change_velo::InstrumentScriptVMFunction_change_velo(InstrumentScriptVM* parent)
1656     : m_vm(parent)
1657     {
1658     }
1659    
1660     VMFnResult* InstrumentScriptVMFunction_change_velo::exec(VMFnArgs* args) {
1661     AbstractEngineChannel* pEngineChannel =
1662     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1663    
1664     const ScriptID id = args->arg(0)->asInt()->evalInt();
1665     if (!id) {
1666     wrnMsg("change_velo(): note ID for argument 1 may not be zero");
1667     return successResult();
1668     }
1669     if (!id.isNoteID()) {
1670     wrnMsg("change_velo(): argument 1 is not a note ID");
1671     return successResult();
1672     }
1673    
1674     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1675     if (!pNote) return successResult();
1676    
1677     const int value = args->arg(1)->asInt()->evalInt();
1678     if (value < 0 || value > 127) {
1679     wrnMsg("change_velo(): velocity of argument 2 is out of range");
1680     return successResult();
1681     }
1682    
1683     if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1684     pNote->cause.Param.Note.Velocity = value;
1685     m_vm->m_event->cause.Param.Note.Velocity = value;
1686 schoenebeck 3216 } else {
1687 schoenebeck 3214 wrnMsg("change_velo(): velocity can only be changed when note is new");
1688     }
1689    
1690     return successResult();
1691     }
1692    
1693 schoenebeck 2935 // event_status() function
1694    
1695     InstrumentScriptVMFunction_event_status::InstrumentScriptVMFunction_event_status(InstrumentScriptVM* parent)
1696     : m_vm(parent)
1697     {
1698     }
1699    
1700     VMFnResult* InstrumentScriptVMFunction_event_status::exec(VMFnArgs* args) {
1701     AbstractEngineChannel* pEngineChannel =
1702     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1703    
1704     const ScriptID id = args->arg(0)->asInt()->evalInt();
1705     if (!id) {
1706     wrnMsg("event_status(): note ID for argument 1 may not be zero");
1707     return successResult(EVENT_STATUS_INACTIVE);
1708     }
1709     if (!id.isNoteID()) {
1710     wrnMsg("event_status(): argument 1 is not a note ID");
1711     return successResult(EVENT_STATUS_INACTIVE);
1712     }
1713    
1714     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1715     return successResult(pNote ? EVENT_STATUS_NOTE_QUEUE : EVENT_STATUS_INACTIVE);
1716     }
1717    
1718 schoenebeck 2948 // wait() function (overrides core wait() implementation)
1719    
1720     InstrumentScriptVMFunction_wait::InstrumentScriptVMFunction_wait(InstrumentScriptVM* parent)
1721     : CoreVMFunction_wait(parent)
1722     {
1723     }
1724    
1725     VMFnResult* InstrumentScriptVMFunction_wait::exec(VMFnArgs* args) {
1726     InstrumentScriptVM* m_vm = (InstrumentScriptVM*) vm;
1727    
1728     // this might be set by passing 1 with the 2nd argument of built-in stop_wait() function
1729     if (m_vm->m_event->ignoreAllWaitCalls) return successResult();
1730    
1731     return CoreVMFunction_wait::exec(args);
1732     }
1733    
1734     // stop_wait() function
1735    
1736     InstrumentScriptVMFunction_stop_wait::InstrumentScriptVMFunction_stop_wait(InstrumentScriptVM* parent)
1737     : m_vm(parent)
1738     {
1739     }
1740    
1741     VMFnResult* InstrumentScriptVMFunction_stop_wait::exec(VMFnArgs* args) {
1742     AbstractEngineChannel* pEngineChannel =
1743     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1744    
1745     const script_callback_id_t id = args->arg(0)->asInt()->evalInt();
1746     if (!id) {
1747     wrnMsg("stop_wait(): callback ID for argument 1 may not be zero");
1748     return successResult();
1749     }
1750    
1751     RTList<ScriptEvent>::Iterator itCallback = pEngineChannel->ScriptCallbackByID(id);
1752     if (!itCallback) return successResult(); // ignore if callback is i.e. not alive anymore
1753    
1754     const bool disableWaitForever =
1755     (args->argsCount() >= 2) ? (args->arg(1)->asInt()->evalInt() == 1) : false;
1756    
1757     pEngineChannel->ScheduleResumeOfScriptCallback(
1758 schoenebeck 3205 itCallback, m_vm->m_event->scheduleTime, disableWaitForever
1759 schoenebeck 2948 );
1760    
1761     return successResult();
1762     }
1763    
1764 schoenebeck 2596 } // namespace LinuxSampler

  ViewVC Help
Powered by ViewVC