/[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 3317 - (hide annotations) (download)
Thu Jul 20 12:18:45 2017 UTC (6 years, 8 months ago) by schoenebeck
File size: 87152 byte(s)
- Minor adjustment to previous commit: don't limit the max.
  value of new script function "change_sustain()".

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

  ViewVC Help
Powered by ViewVC