/[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 3118 - (hide annotations) (download)
Fri Apr 21 13:33:03 2017 UTC (7 months ago) by schoenebeck
File size: 53222 byte(s)
* NKSP: Fixed crash when using built-in script array variable "%ALL_EVENTS".
* NKSP: Added built-in function "change_amp_lfo_depth()".
* NKSP: Added built-in function "change_amp_lfo_freq()".
* NKSP: Added built-in function "change_pitch_lfo_depth()".
* NKSP: Added built-in function "change_pitch_lfo_freq()".
* Bumped version (2.0.0.svn44).

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 2630 if (args->arg(0)->exprType() == INT_EXPR) {
140 schoenebeck 2879 const ScriptID id = args->arg(0)->asInt()->evalInt();
141     if (!id) {
142     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     return INT_EXPR;
363     }
364    
365     VMFnResult* InstrumentScriptVMFunction_change_vol::exec(VMFnArgs* args) {
366     int volume = args->arg(1)->asInt()->evalInt(); // volume change in milli dB
367     bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;
368 schoenebeck 2962 const float fVolumeLin = RTMath::DecibelToLinRatio(float(volume) / 1000.f);
369 schoenebeck 2931
370     AbstractEngineChannel* pEngineChannel =
371     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
372    
373     if (args->arg(0)->exprType() == INT_EXPR) {
374     const ScriptID id = args->arg(0)->asInt()->evalInt();
375     if (!id) {
376     wrnMsg("change_vol(): note ID for argument 1 may not be zero");
377     return successResult();
378     }
379     if (!id.isNoteID()) {
380     wrnMsg("change_vol(): argument 1 is not a note ID");
381     return successResult();
382     }
383    
384     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
385     if (!pNote) return successResult();
386    
387 schoenebeck 2962 // if change_vol() was called immediately after note was triggered
388     // then immediately apply the volume to note object
389     if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
390     if (relative)
391 schoenebeck 2931 pNote->Override.Volume *= fVolumeLin;
392     else
393 schoenebeck 2962 pNote->Override.Volume = fVolumeLin;
394     } else { // otherwise schedule the volume change ...
395 schoenebeck 2931 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
396     e.Init(); // clear IDs
397     e.Type = Event::type_note_synth_param;
398     e.Param.NoteSynthParam.NoteID = id.noteID();
399     e.Param.NoteSynthParam.Type = Event::synth_param_volume;
400     e.Param.NoteSynthParam.Delta = fVolumeLin;
401     e.Param.NoteSynthParam.Relative = relative;
402    
403     pEngineChannel->ScheduleEventMicroSec(&e, 0);
404     }
405 schoenebeck 2962 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
406     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
407     for (int i = 0; i < ids->arraySize(); ++i) {
408     const ScriptID id = ids->evalIntElement(i);
409     if (!id || !id.isNoteID()) continue;
410    
411     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
412     if (!pNote) continue;
413    
414     // if change_vol() was called immediately after note was triggered
415     // then immediately apply the volume to Note object
416     if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
417     if (relative)
418     pNote->Override.Volume *= fVolumeLin;
419     else
420     pNote->Override.Volume = fVolumeLin;
421     } else { // otherwise schedule the volume change ...
422     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
423     e.Init(); // clear IDs
424     e.Type = Event::type_note_synth_param;
425     e.Param.NoteSynthParam.NoteID = id.noteID();
426     e.Param.NoteSynthParam.Type = Event::synth_param_volume;
427     e.Param.NoteSynthParam.Delta = fVolumeLin;
428     e.Param.NoteSynthParam.Relative = relative;
429    
430     pEngineChannel->ScheduleEventMicroSec(&e, 0);
431     }
432     }
433 schoenebeck 2931 }
434    
435     return successResult();
436     }
437    
438     // change_tune() function
439    
440     InstrumentScriptVMFunction_change_tune::InstrumentScriptVMFunction_change_tune(InstrumentScriptVM* parent)
441     : m_vm(parent)
442     {
443     }
444    
445     bool InstrumentScriptVMFunction_change_tune::acceptsArgType(int iArg, ExprType_t type) const {
446     if (iArg == 0)
447     return type == INT_EXPR || type == INT_ARR_EXPR;
448     else
449     return INT_EXPR;
450     }
451    
452     VMFnResult* InstrumentScriptVMFunction_change_tune::exec(VMFnArgs* args) {
453     int tune = args->arg(1)->asInt()->evalInt(); // tuning change in milli cents
454     bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;
455 schoenebeck 2962 const float fFreqRatio = RTMath::CentsToFreqRatioUnlimited(float(tune) / 1000.f);
456 schoenebeck 2931
457     AbstractEngineChannel* pEngineChannel =
458     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
459    
460     if (args->arg(0)->exprType() == INT_EXPR) {
461     const ScriptID id = args->arg(0)->asInt()->evalInt();
462     if (!id) {
463     wrnMsg("change_tune(): note ID for argument 1 may not be zero");
464     return successResult();
465     }
466     if (!id.isNoteID()) {
467     wrnMsg("change_tune(): argument 1 is not a note ID");
468     return successResult();
469     }
470    
471     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
472     if (!pNote) return successResult();
473    
474 schoenebeck 2962 // if change_tune() was called immediately after note was triggered
475     // then immediately apply the tuning to Note object
476     if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
477     if (relative)
478 schoenebeck 2931 pNote->Override.Pitch *= fFreqRatio;
479     else
480 schoenebeck 2962 pNote->Override.Pitch = fFreqRatio;
481     } else { // otherwise schedule tuning change ...
482 schoenebeck 2931 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
483     e.Init(); // clear IDs
484     e.Type = Event::type_note_synth_param;
485     e.Param.NoteSynthParam.NoteID = id.noteID();
486     e.Param.NoteSynthParam.Type = Event::synth_param_pitch;
487     e.Param.NoteSynthParam.Delta = fFreqRatio;
488     e.Param.NoteSynthParam.Relative = relative;
489    
490     pEngineChannel->ScheduleEventMicroSec(&e, 0);
491     }
492 schoenebeck 2962 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
493     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
494     for (int i = 0; i < ids->arraySize(); ++i) {
495     const ScriptID id = ids->evalIntElement(i);
496     if (!id || !id.isNoteID()) continue;
497    
498     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
499     if (!pNote) continue;
500    
501     // if change_tune() was called immediately after note was triggered
502     // then immediately apply the tuning to Note object
503     if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
504     if (relative)
505     pNote->Override.Pitch *= fFreqRatio;
506     else
507     pNote->Override.Pitch = fFreqRatio;
508     } else { // otherwise schedule tuning change ...
509     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
510     e.Init(); // clear IDs
511     e.Type = Event::type_note_synth_param;
512     e.Param.NoteSynthParam.NoteID = id.noteID();
513     e.Param.NoteSynthParam.Type = Event::synth_param_pitch;
514     e.Param.NoteSynthParam.Delta = fFreqRatio;
515     e.Param.NoteSynthParam.Relative = relative;
516    
517     pEngineChannel->ScheduleEventMicroSec(&e, 0);
518     }
519     }
520 schoenebeck 2931 }
521    
522     return successResult();
523     }
524    
525     // change_pan() function
526    
527     InstrumentScriptVMFunction_change_pan::InstrumentScriptVMFunction_change_pan(InstrumentScriptVM* parent)
528     : m_vm(parent)
529     {
530     }
531    
532     bool InstrumentScriptVMFunction_change_pan::acceptsArgType(int iArg, ExprType_t type) const {
533     if (iArg == 0)
534     return type == INT_EXPR || type == INT_ARR_EXPR;
535     else
536     return INT_EXPR;
537     }
538    
539     VMFnResult* InstrumentScriptVMFunction_change_pan::exec(VMFnArgs* args) {
540     int pan = args->arg(1)->asInt()->evalInt();
541     bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;
542    
543     if (pan > 1000) {
544     wrnMsg("change_pan(): argument 2 may not be larger than 1000");
545     pan = 1000;
546     } else if (pan < -1000) {
547     wrnMsg("change_pan(): argument 2 may not be smaller than -1000");
548     pan = -1000;
549     }
550 schoenebeck 2962 const float fPan = float(pan) / 1000.f;
551 schoenebeck 2931
552     AbstractEngineChannel* pEngineChannel =
553     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
554    
555     if (args->arg(0)->exprType() == INT_EXPR) {
556     const ScriptID id = args->arg(0)->asInt()->evalInt();
557     if (!id) {
558     wrnMsg("change_pan(): note ID for argument 1 may not be zero");
559     return successResult();
560     }
561     if (!id.isNoteID()) {
562     wrnMsg("change_pan(): argument 1 is not a note ID");
563     return successResult();
564     }
565    
566     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
567     if (!pNote) return successResult();
568    
569 schoenebeck 2962 // if change_pan() was called immediately after note was triggered
570     // then immediately apply the panning to Note object
571     if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
572     if (relative) {
573 schoenebeck 2931 pNote->Override.Pan = RTMath::RelativeSummedAvg(pNote->Override.Pan, fPan, ++pNote->Override.PanSources);
574     } else {
575     pNote->Override.Pan = fPan;
576     pNote->Override.PanSources = 1; // only relevant on subsequent change_pan() calls on same note with 'relative' being set
577 schoenebeck 2962 }
578     } else { // otherwise schedule panning change ...
579 schoenebeck 2931 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
580     e.Init(); // clear IDs
581     e.Type = Event::type_note_synth_param;
582     e.Param.NoteSynthParam.NoteID = id.noteID();
583     e.Param.NoteSynthParam.Type = Event::synth_param_pan;
584     e.Param.NoteSynthParam.Delta = fPan;
585     e.Param.NoteSynthParam.Relative = relative;
586    
587     pEngineChannel->ScheduleEventMicroSec(&e, 0);
588     }
589 schoenebeck 2962 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
590     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
591     for (int i = 0; i < ids->arraySize(); ++i) {
592     const ScriptID id = ids->evalIntElement(i);
593     if (!id || !id.isNoteID()) continue;
594    
595     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
596     if (!pNote) continue;
597    
598     // if change_pan() was called immediately after note was triggered
599     // then immediately apply the panning to Note object
600     if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
601     if (relative) {
602     pNote->Override.Pan = RTMath::RelativeSummedAvg(pNote->Override.Pan, fPan, ++pNote->Override.PanSources);
603     } else {
604     pNote->Override.Pan = fPan;
605     pNote->Override.PanSources = 1; // only relevant on subsequent change_pan() calls on same note with 'relative' being set
606     }
607     } else { // otherwise schedule panning change ...
608     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
609     e.Init(); // clear IDs
610     e.Type = Event::type_note_synth_param;
611     e.Param.NoteSynthParam.NoteID = id.noteID();
612     e.Param.NoteSynthParam.Type = Event::synth_param_pan;
613     e.Param.NoteSynthParam.Delta = fPan;
614     e.Param.NoteSynthParam.Relative = relative;
615    
616     pEngineChannel->ScheduleEventMicroSec(&e, 0);
617     }
618     }
619 schoenebeck 2931 }
620    
621     return successResult();
622     }
623    
624 schoenebeck 2935 #define VM_FILTER_PAR_MAX_VALUE 1000000
625 schoenebeck 2953 #define VM_EG_PAR_MAX_VALUE 1000000
626 schoenebeck 2935
627     // change_cutoff() function
628    
629     InstrumentScriptVMFunction_change_cutoff::InstrumentScriptVMFunction_change_cutoff(InstrumentScriptVM* parent)
630     : m_vm(parent)
631     {
632     }
633    
634     bool InstrumentScriptVMFunction_change_cutoff::acceptsArgType(int iArg, ExprType_t type) const {
635     if (iArg == 0)
636     return type == INT_EXPR || type == INT_ARR_EXPR;
637     else
638     return INT_EXPR;
639     }
640    
641     VMFnResult* InstrumentScriptVMFunction_change_cutoff::exec(VMFnArgs* args) {
642     int cutoff = args->arg(1)->asInt()->evalInt();
643     if (cutoff > VM_FILTER_PAR_MAX_VALUE) {
644     wrnMsg("change_cutoff(): argument 2 may not be larger than 1000000");
645     cutoff = VM_FILTER_PAR_MAX_VALUE;
646     } else if (cutoff < 0) {
647     wrnMsg("change_cutoff(): argument 2 may not be negative");
648     cutoff = 0;
649     }
650 schoenebeck 2962 const float fCutoff = float(cutoff) / float(VM_FILTER_PAR_MAX_VALUE);
651 schoenebeck 2935
652     AbstractEngineChannel* pEngineChannel =
653     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
654    
655     if (args->arg(0)->exprType() == INT_EXPR) {
656     const ScriptID id = args->arg(0)->asInt()->evalInt();
657     if (!id) {
658     wrnMsg("change_cutoff(): note ID for argument 1 may not be zero");
659     return successResult();
660     }
661     if (!id.isNoteID()) {
662     wrnMsg("change_cutoff(): argument 1 is not a note ID");
663     return successResult();
664     }
665    
666     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
667     if (!pNote) return successResult();
668    
669 schoenebeck 2962 // if change_cutoff() was called immediately after note was triggered
670     // then immediately apply cutoff to Note object
671     if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
672     pNote->Override.Cutoff = fCutoff;
673     } else { // otherwise schedule cutoff change ...
674     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
675     e.Init(); // clear IDs
676     e.Type = Event::type_note_synth_param;
677     e.Param.NoteSynthParam.NoteID = id.noteID();
678     e.Param.NoteSynthParam.Type = Event::synth_param_cutoff;
679     e.Param.NoteSynthParam.Delta = fCutoff;
680     e.Param.NoteSynthParam.Relative = false;
681 schoenebeck 2935
682 schoenebeck 2962 pEngineChannel->ScheduleEventMicroSec(&e, 0);
683     }
684 schoenebeck 2935 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
685     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
686     for (int i = 0; i < ids->arraySize(); ++i) {
687     const ScriptID id = ids->evalIntElement(i);
688     if (!id || !id.isNoteID()) continue;
689    
690     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
691     if (!pNote) continue;
692    
693 schoenebeck 2962 // if change_cutoff() was called immediately after note was triggered
694     // then immediately apply cutoff to Note object
695     if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
696     pNote->Override.Cutoff = fCutoff;
697     } else { // otherwise schedule cutoff change ...
698     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
699     e.Init(); // clear IDs
700     e.Type = Event::type_note_synth_param;
701     e.Param.NoteSynthParam.NoteID = id.noteID();
702     e.Param.NoteSynthParam.Type = Event::synth_param_cutoff;
703     e.Param.NoteSynthParam.Delta = fCutoff;
704     e.Param.NoteSynthParam.Relative = false;
705 schoenebeck 2935
706 schoenebeck 2962 pEngineChannel->ScheduleEventMicroSec(&e, 0);
707     }
708 schoenebeck 2935 }
709     }
710    
711     return successResult();
712     }
713    
714     // change_reso() function
715    
716     InstrumentScriptVMFunction_change_reso::InstrumentScriptVMFunction_change_reso(InstrumentScriptVM* parent)
717     : m_vm(parent)
718     {
719     }
720    
721     bool InstrumentScriptVMFunction_change_reso::acceptsArgType(int iArg, ExprType_t type) const {
722     if (iArg == 0)
723     return type == INT_EXPR || type == INT_ARR_EXPR;
724     else
725     return INT_EXPR;
726     }
727    
728     VMFnResult* InstrumentScriptVMFunction_change_reso::exec(VMFnArgs* args) {
729     int resonance = args->arg(1)->asInt()->evalInt();
730     if (resonance > VM_FILTER_PAR_MAX_VALUE) {
731     wrnMsg("change_reso(): argument 2 may not be larger than 1000000");
732     resonance = VM_FILTER_PAR_MAX_VALUE;
733     } else if (resonance < 0) {
734     wrnMsg("change_reso(): argument 2 may not be negative");
735     resonance = 0;
736     }
737 schoenebeck 2962 const float fResonance = float(resonance) / float(VM_FILTER_PAR_MAX_VALUE);
738 schoenebeck 2935
739     AbstractEngineChannel* pEngineChannel =
740     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
741    
742     if (args->arg(0)->exprType() == INT_EXPR) {
743     const ScriptID id = args->arg(0)->asInt()->evalInt();
744     if (!id) {
745     wrnMsg("change_reso(): note ID for argument 1 may not be zero");
746     return successResult();
747     }
748     if (!id.isNoteID()) {
749     wrnMsg("change_reso(): argument 1 is not a note ID");
750     return successResult();
751     }
752    
753     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
754     if (!pNote) return successResult();
755    
756 schoenebeck 2962 // if change_reso() was called immediately after note was triggered
757     // then immediately apply resonance to Note object
758     if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
759     pNote->Override.Resonance = fResonance;
760     } else { // otherwise schedule resonance change ...
761     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
762     e.Init(); // clear IDs
763     e.Type = Event::type_note_synth_param;
764     e.Param.NoteSynthParam.NoteID = id.noteID();
765     e.Param.NoteSynthParam.Type = Event::synth_param_resonance;
766     e.Param.NoteSynthParam.Delta = fResonance;
767     e.Param.NoteSynthParam.Relative = false;
768 schoenebeck 2935
769 schoenebeck 2962 pEngineChannel->ScheduleEventMicroSec(&e, 0);
770     }
771 schoenebeck 2935 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
772     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
773     for (int i = 0; i < ids->arraySize(); ++i) {
774     const ScriptID id = ids->evalIntElement(i);
775     if (!id || !id.isNoteID()) continue;
776    
777     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
778     if (!pNote) continue;
779    
780 schoenebeck 2962 // if change_reso() was called immediately after note was triggered
781     // then immediately apply resonance to Note object
782     if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
783     pNote->Override.Resonance = fResonance;
784     } else { // otherwise schedule resonance change ...
785     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
786     e.Init(); // clear IDs
787     e.Type = Event::type_note_synth_param;
788     e.Param.NoteSynthParam.NoteID = id.noteID();
789     e.Param.NoteSynthParam.Type = Event::synth_param_resonance;
790     e.Param.NoteSynthParam.Delta = fResonance;
791     e.Param.NoteSynthParam.Relative = false;
792 schoenebeck 2935
793 schoenebeck 2962 pEngineChannel->ScheduleEventMicroSec(&e, 0);
794     }
795 schoenebeck 2935 }
796     }
797    
798     return successResult();
799     }
800 schoenebeck 2953
801     // change_attack() function
802 schoenebeck 2935
803 schoenebeck 2953 InstrumentScriptVMFunction_change_attack::InstrumentScriptVMFunction_change_attack(InstrumentScriptVM* parent)
804     : m_vm(parent)
805     {
806     }
807    
808     bool InstrumentScriptVMFunction_change_attack::acceptsArgType(int iArg, ExprType_t type) const {
809     if (iArg == 0)
810     return type == INT_EXPR || type == INT_ARR_EXPR;
811     else
812     return INT_EXPR;
813     }
814    
815     VMFnResult* InstrumentScriptVMFunction_change_attack::exec(VMFnArgs* args) {
816     int attack = args->arg(1)->asInt()->evalInt();
817     if (attack > VM_EG_PAR_MAX_VALUE) {
818     wrnMsg("change_attack(): argument 2 may not be larger than 1000000");
819     attack = VM_EG_PAR_MAX_VALUE;
820     } else if (attack < 0) {
821     wrnMsg("change_attack(): argument 2 may not be negative");
822     attack = 0;
823     }
824     const float fAttack = float(attack) / float(VM_EG_PAR_MAX_VALUE);
825    
826     AbstractEngineChannel* pEngineChannel =
827     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
828    
829     if (args->arg(0)->exprType() == INT_EXPR) {
830     const ScriptID id = args->arg(0)->asInt()->evalInt();
831     if (!id) {
832     wrnMsg("change_attack(): note ID for argument 1 may not be zero");
833     return successResult();
834     }
835     if (!id.isNoteID()) {
836     wrnMsg("change_attack(): argument 1 is not a note ID");
837     return successResult();
838     }
839    
840     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
841 schoenebeck 2962 if (!pNote) return successResult();
842 schoenebeck 2953
843 schoenebeck 2962 // if change_attack() was called immediately after note was triggered
844     // then immediately apply attack to Note object
845     if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
846     pNote->Override.Attack = fAttack;
847     } else { // otherwise schedule attack change ...
848     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
849     e.Init(); // clear IDs
850     e.Type = Event::type_note_synth_param;
851     e.Param.NoteSynthParam.NoteID = id.noteID();
852     e.Param.NoteSynthParam.Type = Event::synth_param_attack;
853     e.Param.NoteSynthParam.Delta = fAttack;
854     e.Param.NoteSynthParam.Relative = false;
855 schoenebeck 2953
856 schoenebeck 2962 pEngineChannel->ScheduleEventMicroSec(&e, 0);
857     }
858 schoenebeck 2953 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
859     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
860     for (int i = 0; i < ids->arraySize(); ++i) {
861     const ScriptID id = ids->evalIntElement(i);
862     if (!id || !id.isNoteID()) continue;
863    
864     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
865     if (!pNote) continue;
866    
867 schoenebeck 2962 // if change_attack() was called immediately after note was triggered
868     // then immediately apply attack to Note object
869     if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
870     pNote->Override.Attack = fAttack;
871     } else { // otherwise schedule attack change ...
872     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
873     e.Init(); // clear IDs
874     e.Type = Event::type_note_synth_param;
875     e.Param.NoteSynthParam.NoteID = id.noteID();
876     e.Param.NoteSynthParam.Type = Event::synth_param_attack;
877     e.Param.NoteSynthParam.Delta = fAttack;
878     e.Param.NoteSynthParam.Relative = false;
879 schoenebeck 2953
880 schoenebeck 2962 pEngineChannel->ScheduleEventMicroSec(&e, 0);
881     }
882 schoenebeck 2953 }
883     }
884    
885     return successResult();
886     }
887    
888     // change_decay() function
889    
890     InstrumentScriptVMFunction_change_decay::InstrumentScriptVMFunction_change_decay(InstrumentScriptVM* parent)
891     : m_vm(parent)
892     {
893     }
894    
895     bool InstrumentScriptVMFunction_change_decay::acceptsArgType(int iArg, ExprType_t type) const {
896     if (iArg == 0)
897     return type == INT_EXPR || type == INT_ARR_EXPR;
898     else
899     return INT_EXPR;
900     }
901    
902     VMFnResult* InstrumentScriptVMFunction_change_decay::exec(VMFnArgs* args) {
903     int decay = args->arg(1)->asInt()->evalInt();
904     if (decay > VM_EG_PAR_MAX_VALUE) {
905     wrnMsg("change_decay(): argument 2 may not be larger than 1000000");
906     decay = VM_EG_PAR_MAX_VALUE;
907     } else if (decay < 0) {
908     wrnMsg("change_decay(): argument 2 may not be negative");
909     decay = 0;
910     }
911     const float fDecay = float(decay) / float(VM_EG_PAR_MAX_VALUE);
912    
913     AbstractEngineChannel* pEngineChannel =
914     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
915    
916     if (args->arg(0)->exprType() == INT_EXPR) {
917     const ScriptID id = args->arg(0)->asInt()->evalInt();
918     if (!id) {
919     wrnMsg("change_decay(): note ID for argument 1 may not be zero");
920     return successResult();
921     }
922     if (!id.isNoteID()) {
923     wrnMsg("change_decay(): argument 1 is not a note ID");
924     return successResult();
925     }
926    
927     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
928 schoenebeck 2962 if (!pNote) return successResult();
929 schoenebeck 2953
930 schoenebeck 2962 // if change_decay() was called immediately after note was triggered
931     // then immediately apply decay to Note object
932     if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
933     pNote->Override.Decay = fDecay;
934     } else { // otherwise schedule decay change ...
935     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
936     e.Init(); // clear IDs
937     e.Type = Event::type_note_synth_param;
938     e.Param.NoteSynthParam.NoteID = id.noteID();
939     e.Param.NoteSynthParam.Type = Event::synth_param_decay;
940     e.Param.NoteSynthParam.Delta = fDecay;
941     e.Param.NoteSynthParam.Relative = false;
942 schoenebeck 2953
943 schoenebeck 2962 pEngineChannel->ScheduleEventMicroSec(&e, 0);
944     }
945 schoenebeck 2953 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
946     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
947     for (int i = 0; i < ids->arraySize(); ++i) {
948     const ScriptID id = ids->evalIntElement(i);
949     if (!id || !id.isNoteID()) continue;
950    
951     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
952     if (!pNote) continue;
953    
954 schoenebeck 2962 // if change_decay() was called immediately after note was triggered
955     // then immediately apply decay to Note object
956     if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
957     pNote->Override.Decay = fDecay;
958     } else { // otherwise schedule decay change ...
959     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
960     e.Init(); // clear IDs
961     e.Type = Event::type_note_synth_param;
962     e.Param.NoteSynthParam.NoteID = id.noteID();
963     e.Param.NoteSynthParam.Type = Event::synth_param_decay;
964     e.Param.NoteSynthParam.Delta = fDecay;
965     e.Param.NoteSynthParam.Relative = false;
966 schoenebeck 2953
967 schoenebeck 2962 pEngineChannel->ScheduleEventMicroSec(&e, 0);
968     }
969 schoenebeck 2953 }
970     }
971    
972     return successResult();
973     }
974    
975     // change_release() function
976    
977     InstrumentScriptVMFunction_change_release::InstrumentScriptVMFunction_change_release(InstrumentScriptVM* parent)
978     : m_vm(parent)
979     {
980     }
981    
982     bool InstrumentScriptVMFunction_change_release::acceptsArgType(int iArg, ExprType_t type) const {
983     if (iArg == 0)
984     return type == INT_EXPR || type == INT_ARR_EXPR;
985     else
986     return INT_EXPR;
987     }
988    
989     VMFnResult* InstrumentScriptVMFunction_change_release::exec(VMFnArgs* args) {
990     int release = args->arg(1)->asInt()->evalInt();
991     if (release > VM_EG_PAR_MAX_VALUE) {
992     wrnMsg("change_release(): argument 2 may not be larger than 1000000");
993     release = VM_EG_PAR_MAX_VALUE;
994     } else if (release < 0) {
995     wrnMsg("change_release(): argument 2 may not be negative");
996     release = 0;
997     }
998     const float fRelease = float(release) / float(VM_EG_PAR_MAX_VALUE);
999    
1000     AbstractEngineChannel* pEngineChannel =
1001     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1002    
1003     if (args->arg(0)->exprType() == INT_EXPR) {
1004     const ScriptID id = args->arg(0)->asInt()->evalInt();
1005     if (!id) {
1006     wrnMsg("change_release(): note ID for argument 1 may not be zero");
1007     return successResult();
1008     }
1009     if (!id.isNoteID()) {
1010     wrnMsg("change_release(): argument 1 is not a note ID");
1011     return successResult();
1012     }
1013    
1014     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1015 schoenebeck 2962 if (!pNote) return successResult();
1016 schoenebeck 2953
1017 schoenebeck 2962 // if change_release() was called immediately after note was triggered
1018     // then immediately apply relase to Note object
1019     if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
1020     pNote->Override.Release = fRelease;
1021     } else { // otherwise schedule release change ...
1022     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1023     e.Init(); // clear IDs
1024     e.Type = Event::type_note_synth_param;
1025     e.Param.NoteSynthParam.NoteID = id.noteID();
1026     e.Param.NoteSynthParam.Type = Event::synth_param_release;
1027     e.Param.NoteSynthParam.Delta = fRelease;
1028     e.Param.NoteSynthParam.Relative = false;
1029 schoenebeck 2953
1030 schoenebeck 2962 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1031     }
1032 schoenebeck 2953 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1033     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1034     for (int i = 0; i < ids->arraySize(); ++i) {
1035     const ScriptID id = ids->evalIntElement(i);
1036     if (!id || !id.isNoteID()) continue;
1037    
1038     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1039     if (!pNote) continue;
1040    
1041 schoenebeck 2962 // if change_release() was called immediately after note was triggered
1042     // then immediately apply relase to Note object
1043     if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
1044     pNote->Override.Release = fRelease;
1045     } else { // otherwise schedule release change ...
1046     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1047     e.Init(); // clear IDs
1048     e.Type = Event::type_note_synth_param;
1049     e.Param.NoteSynthParam.NoteID = id.noteID();
1050     e.Param.NoteSynthParam.Type = Event::synth_param_release;
1051     e.Param.NoteSynthParam.Delta = fRelease;
1052     e.Param.NoteSynthParam.Relative = false;
1053 schoenebeck 2953
1054 schoenebeck 2962 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1055     }
1056 schoenebeck 2953 }
1057     }
1058    
1059     return successResult();
1060     }
1061    
1062 schoenebeck 3118 #define VM_GENERAL_CHANGE_SYNTH_PAR_MAX_VALUE 1000000
1063    
1064     bool VMChangeSynthParamFunction::acceptsArgType(int iArg, ExprType_t type) const {
1065     if (iArg == 0)
1066     return type == INT_EXPR || type == INT_ARR_EXPR;
1067     else
1068     return INT_EXPR;
1069     }
1070    
1071     template<float NoteBase::_Override::*T_noteParam, int T_synthParam>
1072     VMFnResult* VMChangeSynthParamFunction::execTemplate(VMFnArgs* args, const char* functionName) {
1073     int value = args->arg(1)->asInt()->evalInt();
1074     if (value > VM_GENERAL_CHANGE_SYNTH_PAR_MAX_VALUE) {
1075     wrnMsg(String(functionName) + "(): argument 2 may not be larger than 1000000");
1076     value = VM_GENERAL_CHANGE_SYNTH_PAR_MAX_VALUE;
1077     } else if (value < 0) {
1078     wrnMsg(String(functionName) + "(): argument 2 may not be negative");
1079     value = 0;
1080     }
1081     const float fValue = float(value) / float(VM_GENERAL_CHANGE_SYNTH_PAR_MAX_VALUE);
1082    
1083     AbstractEngineChannel* pEngineChannel =
1084     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1085    
1086     if (args->arg(0)->exprType() == INT_EXPR) {
1087     const ScriptID id = args->arg(0)->asInt()->evalInt();
1088     if (!id) {
1089     wrnMsg(String(functionName) + "(): note ID for argument 1 may not be zero");
1090     return successResult();
1091     }
1092     if (!id.isNoteID()) {
1093     wrnMsg(String(functionName) + "(): argument 1 is not a note ID");
1094     return successResult();
1095     }
1096    
1097     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1098     if (!pNote) return successResult();
1099    
1100     // if this change_*() script function was called immediately after
1101     // note was triggered then immediately apply the synth parameter
1102     // change to Note object
1103     if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
1104     pNote->Override.*T_noteParam = fValue;
1105     } else { // otherwise schedule this synth parameter change ...
1106     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1107     e.Init(); // clear IDs
1108     e.Type = Event::type_note_synth_param;
1109     e.Param.NoteSynthParam.NoteID = id.noteID();
1110     e.Param.NoteSynthParam.Type = (Event::synth_param_t) T_synthParam;
1111     e.Param.NoteSynthParam.Delta = fValue;
1112     e.Param.NoteSynthParam.Relative = false;
1113    
1114     pEngineChannel->ScheduleEventMicroSec(&e, 0);
1115     }
1116     } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1117     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1118     for (int i = 0; i < ids->arraySize(); ++i) {
1119     const ScriptID id = ids->evalIntElement(i);
1120     if (!id || !id.isNoteID()) continue;
1121    
1122     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1123     if (!pNote) continue;
1124    
1125     // if this change_*() script function was called immediately after
1126     // note was triggered then immediately apply the synth parameter
1127     // change to Note object
1128     if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
1129     pNote->Override.*T_noteParam = fValue;
1130     } else { // otherwise schedule this synth parameter change ...
1131     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1132     e.Init(); // clear IDs
1133     e.Type = Event::type_note_synth_param;
1134     e.Param.NoteSynthParam.NoteID = id.noteID();
1135     e.Param.NoteSynthParam.Type = (Event::synth_param_t) T_synthParam;
1136     e.Param.NoteSynthParam.Delta = fValue;
1137     e.Param.NoteSynthParam.Relative = false;
1138    
1139     pEngineChannel->ScheduleEventMicroSec(&e, 0);
1140     }
1141     }
1142     }
1143    
1144     return successResult();
1145     }
1146    
1147     // change_amp_lfo_depth() function
1148    
1149     VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_depth::exec(VMFnArgs* args) {
1150     return VMChangeSynthParamFunction::execTemplate< &NoteBase::_Override::AmpLFODepth, Event::synth_param_amp_lfo_depth >(args, "change_amp_lfo_depth");
1151     }
1152    
1153     // change_amp_lfo_freq() function
1154    
1155     VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_freq::exec(VMFnArgs* args) {
1156     return VMChangeSynthParamFunction::execTemplate< &NoteBase::_Override::AmpLFOFreq, Event::synth_param_amp_lfo_freq >(args, "change_amp_lfo_freq");
1157     }
1158    
1159     // change_pitch_lfo_depth() function
1160    
1161     VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_depth::exec(VMFnArgs* args) {
1162     return VMChangeSynthParamFunction::execTemplate< &NoteBase::_Override::PitchLFODepth, Event::synth_param_pitch_lfo_depth >(args, "change_pitch_lfo_depth");
1163     }
1164    
1165     // change_pitch_lfo_freq() function
1166    
1167     VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_freq::exec(VMFnArgs* args) {
1168     return VMChangeSynthParamFunction::execTemplate< &NoteBase::_Override::PitchLFOFreq, Event::synth_param_pitch_lfo_freq >(args, "change_pitch_lfo_freq");
1169     }
1170    
1171 schoenebeck 2935 // event_status() function
1172    
1173     InstrumentScriptVMFunction_event_status::InstrumentScriptVMFunction_event_status(InstrumentScriptVM* parent)
1174     : m_vm(parent)
1175     {
1176     }
1177    
1178     VMFnResult* InstrumentScriptVMFunction_event_status::exec(VMFnArgs* args) {
1179     AbstractEngineChannel* pEngineChannel =
1180     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1181    
1182     const ScriptID id = args->arg(0)->asInt()->evalInt();
1183     if (!id) {
1184     wrnMsg("event_status(): note ID for argument 1 may not be zero");
1185     return successResult(EVENT_STATUS_INACTIVE);
1186     }
1187     if (!id.isNoteID()) {
1188     wrnMsg("event_status(): argument 1 is not a note ID");
1189     return successResult(EVENT_STATUS_INACTIVE);
1190     }
1191    
1192     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1193     return successResult(pNote ? EVENT_STATUS_NOTE_QUEUE : EVENT_STATUS_INACTIVE);
1194     }
1195    
1196 schoenebeck 2948 // wait() function (overrides core wait() implementation)
1197    
1198     InstrumentScriptVMFunction_wait::InstrumentScriptVMFunction_wait(InstrumentScriptVM* parent)
1199     : CoreVMFunction_wait(parent)
1200     {
1201     }
1202    
1203     VMFnResult* InstrumentScriptVMFunction_wait::exec(VMFnArgs* args) {
1204     InstrumentScriptVM* m_vm = (InstrumentScriptVM*) vm;
1205    
1206     // this might be set by passing 1 with the 2nd argument of built-in stop_wait() function
1207     if (m_vm->m_event->ignoreAllWaitCalls) return successResult();
1208    
1209     return CoreVMFunction_wait::exec(args);
1210     }
1211    
1212     // stop_wait() function
1213    
1214     InstrumentScriptVMFunction_stop_wait::InstrumentScriptVMFunction_stop_wait(InstrumentScriptVM* parent)
1215     : m_vm(parent)
1216     {
1217     }
1218    
1219     VMFnResult* InstrumentScriptVMFunction_stop_wait::exec(VMFnArgs* args) {
1220     AbstractEngineChannel* pEngineChannel =
1221     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1222    
1223     const script_callback_id_t id = args->arg(0)->asInt()->evalInt();
1224     if (!id) {
1225     wrnMsg("stop_wait(): callback ID for argument 1 may not be zero");
1226     return successResult();
1227     }
1228    
1229     RTList<ScriptEvent>::Iterator itCallback = pEngineChannel->ScriptCallbackByID(id);
1230     if (!itCallback) return successResult(); // ignore if callback is i.e. not alive anymore
1231    
1232     const bool disableWaitForever =
1233     (args->argsCount() >= 2) ? (args->arg(1)->asInt()->evalInt() == 1) : false;
1234    
1235     pEngineChannel->ScheduleResumeOfScriptCallback(
1236     itCallback, m_vm->m_event->cause.SchedTime(), disableWaitForever
1237     );
1238    
1239     return successResult();
1240     }
1241    
1242 schoenebeck 2596 } // namespace LinuxSampler

  ViewVC Help
Powered by ViewVC