/[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 3303 - (hide annotations) (download)
Mon Jul 10 17:45:30 2017 UTC (6 years, 9 months ago) by schoenebeck
File size: 86843 byte(s)
* NKSP: Removed max. value limitation of built-in functions
  "change_attack()", "change_decay()" and "change_release()" to
  i.e. allow passing 2000000 for doubling the respective time.
* Bumped version (2.0.0.svn68).

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     // change_amp_lfo_depth() function
1178    
1179     VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_depth::exec(VMFnArgs* args) {
1180 schoenebeck 3188 return VMChangeSynthParamFunction::execTemplate<
1181     &NoteBase::_Override::AmpLFODepth,
1182 schoenebeck 3193 Event::synth_param_amp_lfo_depth,
1183     true, 1000000, 0>( args, "change_amp_lfo_depth" );
1184 schoenebeck 3118 }
1185    
1186     // change_amp_lfo_freq() function
1187    
1188     VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_freq::exec(VMFnArgs* args) {
1189 schoenebeck 3188 return VMChangeSynthParamFunction::execTemplate<
1190     &NoteBase::_Override::AmpLFOFreq,
1191 schoenebeck 3193 Event::synth_param_amp_lfo_freq,
1192     true, 1000000, 0>( args, "change_amp_lfo_freq" );
1193 schoenebeck 3118 }
1194    
1195     // change_pitch_lfo_depth() function
1196    
1197     VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_depth::exec(VMFnArgs* args) {
1198 schoenebeck 3188 return VMChangeSynthParamFunction::execTemplate<
1199     &NoteBase::_Override::PitchLFODepth,
1200 schoenebeck 3193 Event::synth_param_pitch_lfo_depth,
1201     true, 1000000, 0>( args, "change_pitch_lfo_depth" );
1202 schoenebeck 3118 }
1203    
1204     // change_pitch_lfo_freq() function
1205    
1206     VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_freq::exec(VMFnArgs* args) {
1207 schoenebeck 3188 return VMChangeSynthParamFunction::execTemplate<
1208     &NoteBase::_Override::PitchLFOFreq,
1209 schoenebeck 3193 Event::synth_param_pitch_lfo_freq,
1210     true, 1000000, 0>( args, "change_pitch_lfo_freq" );
1211 schoenebeck 3118 }
1212    
1213 schoenebeck 3188 // change_vol_time() function
1214    
1215     VMFnResult* InstrumentScriptVMFunction_change_vol_time::exec(VMFnArgs* args) {
1216     return VMChangeSynthParamFunction::execTemplate<
1217     &NoteBase::_Override::VolumeTime,
1218 schoenebeck 3193 Event::synth_param_volume_time,
1219     false, NO_LIMIT, 0>( args, "change_vol_time" );
1220 schoenebeck 3188 }
1221    
1222     // change_tune_time() function
1223    
1224     VMFnResult* InstrumentScriptVMFunction_change_tune_time::exec(VMFnArgs* args) {
1225     return VMChangeSynthParamFunction::execTemplate<
1226     &NoteBase::_Override::PitchTime,
1227 schoenebeck 3193 Event::synth_param_pitch_time,
1228     false, NO_LIMIT, 0>( args, "change_tune_time" );
1229 schoenebeck 3188 }
1230    
1231 schoenebeck 3246 // template for change_*_curve() functions
1232    
1233     bool VMChangeFadeCurveFunction::acceptsArgType(int iArg, ExprType_t type) const {
1234     if (iArg == 0)
1235     return type == INT_EXPR || type == INT_ARR_EXPR;
1236     else
1237     return type == INT_EXPR;
1238     }
1239    
1240     template<fade_curve_t NoteBase::_Override::*T_noteParam, int T_synthParam>
1241     VMFnResult* VMChangeFadeCurveFunction::execTemplate(VMFnArgs* args, const char* functionName) {
1242     int value = args->arg(1)->asInt()->evalInt();
1243     switch (value) {
1244     case FADE_CURVE_LINEAR:
1245     case FADE_CURVE_EASE_IN_EASE_OUT:
1246     break;
1247     default:
1248     wrnMsg(String(functionName) + "(): invalid curve type passed as argument 2");
1249     return successResult();
1250     }
1251    
1252     AbstractEngineChannel* pEngineChannel =
1253     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1254    
1255     if (args->arg(0)->exprType() == INT_EXPR) {
1256     const ScriptID id = args->arg(0)->asInt()->evalInt();
1257     if (!id) {
1258     wrnMsg(String(functionName) + "(): note ID for argument 1 may not be zero");
1259     return successResult();
1260     }
1261     if (!id.isNoteID()) {
1262     wrnMsg(String(functionName) + "(): argument 1 is not a note ID");
1263     return successResult();
1264     }
1265    
1266     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1267     if (!pNote) return successResult();
1268    
1269     // if this change_*_curve() script function was called immediately after
1270     // note was triggered then immediately apply the synth parameter
1271     // change to Note object
1272     if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1273     pNote->Override.*T_noteParam = (fade_curve_t) value;
1274     } else { // otherwise schedule this synth parameter change ...
1275     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1276     e.Init(); // clear IDs
1277     e.Type = Event::type_note_synth_param;
1278     e.Param.NoteSynthParam.NoteID = id.noteID();
1279     e.Param.NoteSynthParam.Type = (Event::synth_param_t) T_synthParam;
1280     e.Param.NoteSynthParam.Delta = value;
1281     e.Param.NoteSynthParam.Relative = false;
1282    
1283     pEngineChannel->ScheduleEventMicroSec(&e, 0);
1284     }
1285     } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1286     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1287     for (int i = 0; i < ids->arraySize(); ++i) {
1288     const ScriptID id = ids->evalIntElement(i);
1289     if (!id || !id.isNoteID()) continue;
1290    
1291     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1292     if (!pNote) continue;
1293    
1294     // if this change_*_curve() script function was called immediately after
1295     // note was triggered then immediately apply the synth parameter
1296     // change to Note object
1297     if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1298     pNote->Override.*T_noteParam = (fade_curve_t) value;
1299     } else { // otherwise schedule this synth parameter change ...
1300     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1301     e.Init(); // clear IDs
1302     e.Type = Event::type_note_synth_param;
1303     e.Param.NoteSynthParam.NoteID = id.noteID();
1304     e.Param.NoteSynthParam.Type = (Event::synth_param_t) T_synthParam;
1305     e.Param.NoteSynthParam.Delta = value;
1306     e.Param.NoteSynthParam.Relative = false;
1307    
1308     pEngineChannel->ScheduleEventMicroSec(&e, 0);
1309     }
1310     }
1311     }
1312    
1313     return successResult();
1314     }
1315    
1316     // change_vol_curve() function
1317    
1318     VMFnResult* InstrumentScriptVMFunction_change_vol_curve::exec(VMFnArgs* args) {
1319     return VMChangeFadeCurveFunction::execTemplate<
1320     &NoteBase::_Override::VolumeCurve,
1321     Event::synth_param_volume_curve>( args, "change_vol_curve" );
1322     }
1323    
1324     // change_tune_curve() function
1325    
1326     VMFnResult* InstrumentScriptVMFunction_change_tune_curve::exec(VMFnArgs* args) {
1327     return VMChangeFadeCurveFunction::execTemplate<
1328     &NoteBase::_Override::PitchCurve,
1329     Event::synth_param_pitch_curve>( args, "change_tune_curve" );
1330     }
1331    
1332 schoenebeck 3188 // fade_in() function
1333    
1334     InstrumentScriptVMFunction_fade_in::InstrumentScriptVMFunction_fade_in(InstrumentScriptVM* parent)
1335     : m_vm(parent)
1336     {
1337     }
1338    
1339     bool InstrumentScriptVMFunction_fade_in::acceptsArgType(int iArg, ExprType_t type) const {
1340     if (iArg == 0)
1341     return type == INT_EXPR || type == INT_ARR_EXPR;
1342     else
1343     return type == INT_EXPR;
1344     }
1345    
1346     VMFnResult* InstrumentScriptVMFunction_fade_in::exec(VMFnArgs* args) {
1347     int duration = args->arg(1)->asInt()->evalInt();
1348     if (duration < 0) {
1349     wrnMsg("fade_in(): argument 2 may not be negative");
1350     duration = 0;
1351     }
1352     const float fDuration = float(duration) / 1000000.f; // convert microseconds to seconds
1353    
1354     AbstractEngineChannel* pEngineChannel =
1355     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1356    
1357     if (args->arg(0)->exprType() == INT_EXPR) {
1358     const ScriptID id = args->arg(0)->asInt()->evalInt();
1359     if (!id) {
1360     wrnMsg("fade_in(): note ID for argument 1 may not be zero");
1361     return successResult();
1362     }
1363     if (!id.isNoteID()) {
1364     wrnMsg("fade_in(): argument 1 is not a note ID");
1365     return successResult();
1366     }
1367    
1368     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1369     if (!pNote) return successResult();
1370    
1371     // if fade_in() was called immediately after note was triggered
1372     // then immediately apply a start volume of zero to Note object,
1373     // as well as the fade in duration
1374 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1375 schoenebeck 3188 pNote->Override.Volume = 0.f;
1376     pNote->Override.VolumeTime = fDuration;
1377     } else { // otherwise schedule a "volume time" change with the requested fade in duration ...
1378     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1379     e.Init(); // clear IDs
1380     e.Type = Event::type_note_synth_param;
1381     e.Param.NoteSynthParam.NoteID = id.noteID();
1382     e.Param.NoteSynthParam.Type = Event::synth_param_volume_time;
1383     e.Param.NoteSynthParam.Delta = fDuration;
1384     e.Param.NoteSynthParam.Relative = false;
1385    
1386     pEngineChannel->ScheduleEventMicroSec(&e, 0);
1387     }
1388     // and finally schedule a "volume" change, simply one time slice
1389     // ahead, with the final fade in volume (1.0)
1390     {
1391     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1392     e.Init(); // clear IDs
1393     e.Type = Event::type_note_synth_param;
1394     e.Param.NoteSynthParam.NoteID = id.noteID();
1395     e.Param.NoteSynthParam.Type = Event::synth_param_volume;
1396     e.Param.NoteSynthParam.Delta = 1.f;
1397     e.Param.NoteSynthParam.Relative = false;
1398    
1399     // scheduling with 0 delay would also work here, but +1 is more
1400     // safe regarding potential future implementation changes of the
1401     // scheduler (see API comments of RTAVLTree::insert())
1402     pEngineChannel->ScheduleEventMicroSec(&e, 1);
1403     }
1404     } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1405     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1406     for (int i = 0; i < ids->arraySize(); ++i) {
1407     const ScriptID id = ids->evalIntElement(i);
1408     if (!id || !id.isNoteID()) continue;
1409    
1410     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1411     if (!pNote) continue;
1412    
1413     // if fade_in() was called immediately after note was triggered
1414     // then immediately apply a start volume of zero to Note object,
1415     // as well as the fade in duration
1416 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1417 schoenebeck 3188 pNote->Override.Volume = 0.f;
1418     pNote->Override.VolumeTime = fDuration;
1419     } else { // otherwise schedule a "volume time" change with the requested fade in duration ...
1420     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1421     e.Init(); // clear IDs
1422     e.Type = Event::type_note_synth_param;
1423     e.Param.NoteSynthParam.NoteID = id.noteID();
1424     e.Param.NoteSynthParam.Type = Event::synth_param_volume_time;
1425     e.Param.NoteSynthParam.Delta = fDuration;
1426     e.Param.NoteSynthParam.Relative = false;
1427    
1428     pEngineChannel->ScheduleEventMicroSec(&e, 0);
1429     }
1430     // and finally schedule a "volume" change, simply one time slice
1431     // ahead, with the final fade in volume (1.0)
1432     {
1433     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1434     e.Init(); // clear IDs
1435     e.Type = Event::type_note_synth_param;
1436     e.Param.NoteSynthParam.NoteID = id.noteID();
1437     e.Param.NoteSynthParam.Type = Event::synth_param_volume;
1438     e.Param.NoteSynthParam.Delta = 1.f;
1439     e.Param.NoteSynthParam.Relative = false;
1440    
1441     // scheduling with 0 delay would also work here, but +1 is more
1442     // safe regarding potential future implementation changes of the
1443     // scheduler (see API comments of RTAVLTree::insert())
1444     pEngineChannel->ScheduleEventMicroSec(&e, 1);
1445     }
1446     }
1447     }
1448    
1449     return successResult();
1450     }
1451    
1452     // fade_out() function
1453    
1454     InstrumentScriptVMFunction_fade_out::InstrumentScriptVMFunction_fade_out(InstrumentScriptVM* parent)
1455     : m_vm(parent)
1456     {
1457     }
1458    
1459     bool InstrumentScriptVMFunction_fade_out::acceptsArgType(int iArg, ExprType_t type) const {
1460     if (iArg == 0)
1461     return type == INT_EXPR || type == INT_ARR_EXPR;
1462     else
1463     return type == INT_EXPR;
1464     }
1465    
1466     VMFnResult* InstrumentScriptVMFunction_fade_out::exec(VMFnArgs* args) {
1467     int duration = args->arg(1)->asInt()->evalInt();
1468     if (duration < 0) {
1469     wrnMsg("fade_out(): argument 2 may not be negative");
1470     duration = 0;
1471     }
1472     const float fDuration = float(duration) / 1000000.f; // convert microseconds to seconds
1473    
1474     bool stop = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : true;
1475    
1476     AbstractEngineChannel* pEngineChannel =
1477     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1478    
1479     if (args->arg(0)->exprType() == INT_EXPR) {
1480     const ScriptID id = args->arg(0)->asInt()->evalInt();
1481     if (!id) {
1482     wrnMsg("fade_out(): note ID for argument 1 may not be zero");
1483     return successResult();
1484     }
1485     if (!id.isNoteID()) {
1486     wrnMsg("fade_out(): argument 1 is not a note ID");
1487     return successResult();
1488     }
1489    
1490     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1491     if (!pNote) return successResult();
1492    
1493     // if fade_out() was called immediately after note was triggered
1494     // then immediately apply fade out duration to Note object
1495 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1496 schoenebeck 3188 pNote->Override.VolumeTime = fDuration;
1497     } else { // otherwise schedule a "volume time" change with the requested fade out duration ...
1498     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1499     e.Init(); // clear IDs
1500     e.Type = Event::type_note_synth_param;
1501     e.Param.NoteSynthParam.NoteID = id.noteID();
1502     e.Param.NoteSynthParam.Type = Event::synth_param_volume_time;
1503     e.Param.NoteSynthParam.Delta = fDuration;
1504     e.Param.NoteSynthParam.Relative = false;
1505    
1506     pEngineChannel->ScheduleEventMicroSec(&e, 0);
1507     }
1508     // now schedule a "volume" change, simply one time slice ahead, with
1509     // the final fade out volume (0.0)
1510     {
1511     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1512     e.Init(); // clear IDs
1513     e.Type = Event::type_note_synth_param;
1514     e.Param.NoteSynthParam.NoteID = id.noteID();
1515     e.Param.NoteSynthParam.Type = Event::synth_param_volume;
1516     e.Param.NoteSynthParam.Delta = 0.f;
1517     e.Param.NoteSynthParam.Relative = false;
1518    
1519     // scheduling with 0 delay would also work here, but +1 is more
1520     // safe regarding potential future implementation changes of the
1521     // scheduler (see API comments of RTAVLTree::insert())
1522     pEngineChannel->ScheduleEventMicroSec(&e, 1);
1523     }
1524     // and finally if stopping the note was requested after the fade out
1525     // completed, then schedule to kill the voice after the requested
1526     // duration
1527     if (stop) {
1528     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1529     e.Init(); // clear IDs
1530     e.Type = Event::type_kill_note;
1531     e.Param.Note.ID = id.noteID();
1532     e.Param.Note.Key = pNote->hostKey;
1533    
1534     pEngineChannel->ScheduleEventMicroSec(&e, duration + 1);
1535     }
1536     } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1537     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1538     for (int i = 0; i < ids->arraySize(); ++i) {
1539     const ScriptID id = ids->evalIntElement(i);
1540     if (!id || !id.isNoteID()) continue;
1541    
1542     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1543     if (!pNote) continue;
1544    
1545     // if fade_out() was called immediately after note was triggered
1546     // then immediately apply fade out duration to Note object
1547 schoenebeck 3205 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1548 schoenebeck 3188 pNote->Override.VolumeTime = fDuration;
1549     } else { // otherwise schedule a "volume time" change with the requested fade out duration ...
1550     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1551     e.Init(); // clear IDs
1552     e.Type = Event::type_note_synth_param;
1553     e.Param.NoteSynthParam.NoteID = id.noteID();
1554     e.Param.NoteSynthParam.Type = Event::synth_param_volume_time;
1555     e.Param.NoteSynthParam.Delta = fDuration;
1556     e.Param.NoteSynthParam.Relative = false;
1557    
1558     pEngineChannel->ScheduleEventMicroSec(&e, 0);
1559     }
1560     // now schedule a "volume" change, simply one time slice ahead, with
1561     // the final fade out volume (0.0)
1562     {
1563     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1564     e.Init(); // clear IDs
1565     e.Type = Event::type_note_synth_param;
1566     e.Param.NoteSynthParam.NoteID = id.noteID();
1567     e.Param.NoteSynthParam.Type = Event::synth_param_volume;
1568     e.Param.NoteSynthParam.Delta = 0.f;
1569     e.Param.NoteSynthParam.Relative = false;
1570    
1571     // scheduling with 0 delay would also work here, but +1 is more
1572     // safe regarding potential future implementation changes of the
1573     // scheduler (see API comments of RTAVLTree::insert())
1574     pEngineChannel->ScheduleEventMicroSec(&e, 1);
1575     }
1576     // and finally if stopping the note was requested after the fade out
1577     // completed, then schedule to kill the voice after the requested
1578     // duration
1579     if (stop) {
1580     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1581     e.Init(); // clear IDs
1582     e.Type = Event::type_kill_note;
1583     e.Param.Note.ID = id.noteID();
1584     e.Param.Note.Key = pNote->hostKey;
1585    
1586     pEngineChannel->ScheduleEventMicroSec(&e, duration + 1);
1587     }
1588     }
1589     }
1590    
1591     return successResult();
1592     }
1593    
1594 schoenebeck 3193 // get_event_par() function
1595    
1596     InstrumentScriptVMFunction_get_event_par::InstrumentScriptVMFunction_get_event_par(InstrumentScriptVM* parent)
1597     : m_vm(parent)
1598     {
1599     }
1600    
1601     VMFnResult* InstrumentScriptVMFunction_get_event_par::exec(VMFnArgs* args) {
1602     AbstractEngineChannel* pEngineChannel =
1603     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1604    
1605     const ScriptID id = args->arg(0)->asInt()->evalInt();
1606     if (!id) {
1607     wrnMsg("get_event_par(): note ID for argument 1 may not be zero");
1608     return successResult(0);
1609     }
1610     if (!id.isNoteID()) {
1611     wrnMsg("get_event_par(): argument 1 is not a note ID");
1612     return successResult(0);
1613     }
1614    
1615     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1616     if (!pNote) {
1617     wrnMsg("get_event_par(): no note alive with that note ID of argument 1");
1618     return successResult(0);
1619     }
1620    
1621     const int parameter = args->arg(1)->asInt()->evalInt();
1622     switch (parameter) {
1623     case EVENT_PAR_NOTE:
1624     return successResult(pNote->cause.Param.Note.Key);
1625     case EVENT_PAR_VELOCITY:
1626     return successResult(pNote->cause.Param.Note.Velocity);
1627     case EVENT_PAR_VOLUME:
1628     return successResult(
1629     RTMath::LinRatioToDecibel(pNote->Override.Volume) * 1000.f
1630     );
1631     case EVENT_PAR_TUNE:
1632     return successResult(
1633     RTMath::FreqRatioToCents(pNote->Override.Pitch) * 1000.f
1634     );
1635     case EVENT_PAR_0:
1636     return successResult(pNote->userPar[0]);
1637     case EVENT_PAR_1:
1638     return successResult(pNote->userPar[1]);
1639     case EVENT_PAR_2:
1640     return successResult(pNote->userPar[2]);
1641     case EVENT_PAR_3:
1642     return successResult(pNote->userPar[3]);
1643     }
1644    
1645     wrnMsg("get_event_par(): argument 2 is an invalid event parameter");
1646     return successResult(0);
1647     }
1648    
1649     // set_event_par() function
1650    
1651     InstrumentScriptVMFunction_set_event_par::InstrumentScriptVMFunction_set_event_par(InstrumentScriptVM* parent)
1652     : m_vm(parent)
1653     {
1654     }
1655    
1656     VMFnResult* InstrumentScriptVMFunction_set_event_par::exec(VMFnArgs* args) {
1657     AbstractEngineChannel* pEngineChannel =
1658     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1659    
1660     const ScriptID id = args->arg(0)->asInt()->evalInt();
1661     if (!id) {
1662     wrnMsg("set_event_par(): note ID for argument 1 may not be zero");
1663     return successResult();
1664     }
1665     if (!id.isNoteID()) {
1666     wrnMsg("set_event_par(): argument 1 is not a note ID");
1667     return successResult();
1668     }
1669    
1670     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1671     if (!pNote) return successResult();
1672    
1673     const int parameter = args->arg(1)->asInt()->evalInt();
1674     const int value = args->arg(2)->asInt()->evalInt();
1675    
1676     switch (parameter) {
1677     case EVENT_PAR_NOTE:
1678     if (value < 0 || value > 127) {
1679     wrnMsg("set_event_par(): note number of argument 3 is out of range");
1680     return successResult();
1681     }
1682 schoenebeck 3214 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1683 schoenebeck 3193 pNote->cause.Param.Note.Key = value;
1684 schoenebeck 3214 m_vm->m_event->cause.Param.Note.Key = value;
1685     } else {
1686 schoenebeck 3193 wrnMsg("set_event_par(): note number can only be changed when note is new");
1687 schoenebeck 3214 }
1688 schoenebeck 3193 return successResult();
1689     case EVENT_PAR_VELOCITY:
1690     if (value < 0 || value > 127) {
1691     wrnMsg("set_event_par(): velocity of argument 3 is out of range");
1692     return successResult();
1693     }
1694 schoenebeck 3214 if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1695 schoenebeck 3193 pNote->cause.Param.Note.Velocity = value;
1696 schoenebeck 3214 m_vm->m_event->cause.Param.Note.Velocity = value;
1697     } else {
1698 schoenebeck 3193 wrnMsg("set_event_par(): velocity can only be changed when note is new");
1699 schoenebeck 3214 }
1700 schoenebeck 3193 return successResult();
1701     case EVENT_PAR_VOLUME:
1702     wrnMsg("set_event_par(): changing volume by this function is currently not supported, use change_vol() instead");
1703     return successResult();
1704     case EVENT_PAR_TUNE:
1705     wrnMsg("set_event_par(): changing tune by this function is currently not supported, use change_tune() instead");
1706     return successResult();
1707     case EVENT_PAR_0:
1708     pNote->userPar[0] = value;
1709     return successResult();
1710     case EVENT_PAR_1:
1711     pNote->userPar[1] = value;
1712     return successResult();
1713     case EVENT_PAR_2:
1714     pNote->userPar[2] = value;
1715     return successResult();
1716     case EVENT_PAR_3:
1717     pNote->userPar[3] = value;
1718     return successResult();
1719     }
1720    
1721     wrnMsg("set_event_par(): argument 2 is an invalid event parameter");
1722     return successResult();
1723     }
1724    
1725 schoenebeck 3214 // change_note() function
1726    
1727     InstrumentScriptVMFunction_change_note::InstrumentScriptVMFunction_change_note(InstrumentScriptVM* parent)
1728     : m_vm(parent)
1729     {
1730     }
1731    
1732     VMFnResult* InstrumentScriptVMFunction_change_note::exec(VMFnArgs* args) {
1733     AbstractEngineChannel* pEngineChannel =
1734     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1735    
1736     const ScriptID id = args->arg(0)->asInt()->evalInt();
1737     if (!id) {
1738     wrnMsg("change_note(): note ID for argument 1 may not be zero");
1739     return successResult();
1740     }
1741     if (!id.isNoteID()) {
1742     wrnMsg("change_note(): argument 1 is not a note ID");
1743     return successResult();
1744     }
1745    
1746     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1747     if (!pNote) return successResult();
1748    
1749     const int value = args->arg(1)->asInt()->evalInt();
1750     if (value < 0 || value > 127) {
1751     wrnMsg("change_note(): note number of argument 2 is out of range");
1752     return successResult();
1753     }
1754    
1755     if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1756     pNote->cause.Param.Note.Key = value;
1757     m_vm->m_event->cause.Param.Note.Key = value;
1758 schoenebeck 3216 } else {
1759 schoenebeck 3214 wrnMsg("change_note(): note number can only be changed when note is new");
1760     }
1761    
1762     return successResult();
1763     }
1764    
1765     // change_velo() function
1766    
1767     InstrumentScriptVMFunction_change_velo::InstrumentScriptVMFunction_change_velo(InstrumentScriptVM* parent)
1768     : m_vm(parent)
1769     {
1770     }
1771    
1772     VMFnResult* InstrumentScriptVMFunction_change_velo::exec(VMFnArgs* args) {
1773     AbstractEngineChannel* pEngineChannel =
1774     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1775    
1776     const ScriptID id = args->arg(0)->asInt()->evalInt();
1777     if (!id) {
1778     wrnMsg("change_velo(): note ID for argument 1 may not be zero");
1779     return successResult();
1780     }
1781     if (!id.isNoteID()) {
1782     wrnMsg("change_velo(): argument 1 is not a note ID");
1783     return successResult();
1784     }
1785    
1786     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1787     if (!pNote) return successResult();
1788    
1789     const int value = args->arg(1)->asInt()->evalInt();
1790     if (value < 0 || value > 127) {
1791     wrnMsg("change_velo(): velocity of argument 2 is out of range");
1792     return successResult();
1793     }
1794    
1795     if (m_vm->m_event->scheduleTime == pNote->triggerSchedTime) {
1796     pNote->cause.Param.Note.Velocity = value;
1797     m_vm->m_event->cause.Param.Note.Velocity = value;
1798 schoenebeck 3216 } else {
1799 schoenebeck 3214 wrnMsg("change_velo(): velocity can only be changed when note is new");
1800     }
1801    
1802     return successResult();
1803     }
1804    
1805 schoenebeck 3255 // change_play_pos() function
1806    
1807     InstrumentScriptVMFunction_change_play_pos::InstrumentScriptVMFunction_change_play_pos(InstrumentScriptVM* parent)
1808     : m_vm(parent)
1809     {
1810     }
1811    
1812     VMFnResult* InstrumentScriptVMFunction_change_play_pos::exec(VMFnArgs* args) {
1813     const ScriptID id = args->arg(0)->asInt()->evalInt();
1814     if (!id) {
1815     wrnMsg("change_play_pos(): note ID for argument 1 may not be zero");
1816     return successResult();
1817     }
1818     if (!id.isNoteID()) {
1819     wrnMsg("change_play_pos(): argument 1 is not a note ID");
1820     return successResult();
1821     }
1822    
1823     const int pos = args->arg(1)->asInt()->evalInt();
1824     if (pos < 0) {
1825     wrnMsg("change_play_pos(): playback position of argument 2 may not be negative");
1826     return successResult();
1827     }
1828    
1829     AbstractEngineChannel* pEngineChannel =
1830     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1831    
1832     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1833     if (!pNote) return successResult();
1834    
1835     pNote->Override.SampleOffset = pos;
1836    
1837     return successResult();
1838     }
1839    
1840 schoenebeck 2935 // event_status() function
1841    
1842     InstrumentScriptVMFunction_event_status::InstrumentScriptVMFunction_event_status(InstrumentScriptVM* parent)
1843     : m_vm(parent)
1844     {
1845     }
1846    
1847     VMFnResult* InstrumentScriptVMFunction_event_status::exec(VMFnArgs* args) {
1848     AbstractEngineChannel* pEngineChannel =
1849     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1850    
1851     const ScriptID id = args->arg(0)->asInt()->evalInt();
1852     if (!id) {
1853     wrnMsg("event_status(): note ID for argument 1 may not be zero");
1854     return successResult(EVENT_STATUS_INACTIVE);
1855     }
1856     if (!id.isNoteID()) {
1857     wrnMsg("event_status(): argument 1 is not a note ID");
1858     return successResult(EVENT_STATUS_INACTIVE);
1859     }
1860    
1861     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1862     return successResult(pNote ? EVENT_STATUS_NOTE_QUEUE : EVENT_STATUS_INACTIVE);
1863     }
1864    
1865 schoenebeck 3296 // callback_status() function
1866    
1867     InstrumentScriptVMFunction_callback_status::InstrumentScriptVMFunction_callback_status(InstrumentScriptVM* parent)
1868     : m_vm(parent)
1869     {
1870     }
1871    
1872     VMFnResult* InstrumentScriptVMFunction_callback_status::exec(VMFnArgs* args) {
1873     const script_callback_id_t id = args->arg(0)->asInt()->evalInt();
1874     if (!id) {
1875     wrnMsg("callback_status(): callback ID for argument 1 may not be zero");
1876     return successResult();
1877     }
1878    
1879     AbstractEngineChannel* pEngineChannel =
1880     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1881    
1882     RTList<ScriptEvent>::Iterator itCallback = pEngineChannel->ScriptCallbackByID(id);
1883     if (!itCallback)
1884     return successResult(CALLBACK_STATUS_TERMINATED);
1885    
1886     return successResult(
1887     (m_vm->m_event->execCtx == itCallback->execCtx) ?
1888     CALLBACK_STATUS_RUNNING : CALLBACK_STATUS_QUEUE
1889     );
1890     }
1891    
1892 schoenebeck 2948 // wait() function (overrides core wait() implementation)
1893    
1894     InstrumentScriptVMFunction_wait::InstrumentScriptVMFunction_wait(InstrumentScriptVM* parent)
1895     : CoreVMFunction_wait(parent)
1896     {
1897     }
1898    
1899     VMFnResult* InstrumentScriptVMFunction_wait::exec(VMFnArgs* args) {
1900     InstrumentScriptVM* m_vm = (InstrumentScriptVM*) vm;
1901    
1902     // this might be set by passing 1 with the 2nd argument of built-in stop_wait() function
1903     if (m_vm->m_event->ignoreAllWaitCalls) return successResult();
1904    
1905     return CoreVMFunction_wait::exec(args);
1906     }
1907    
1908     // stop_wait() function
1909    
1910     InstrumentScriptVMFunction_stop_wait::InstrumentScriptVMFunction_stop_wait(InstrumentScriptVM* parent)
1911     : m_vm(parent)
1912     {
1913     }
1914    
1915     VMFnResult* InstrumentScriptVMFunction_stop_wait::exec(VMFnArgs* args) {
1916     AbstractEngineChannel* pEngineChannel =
1917     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1918    
1919     const script_callback_id_t id = args->arg(0)->asInt()->evalInt();
1920     if (!id) {
1921     wrnMsg("stop_wait(): callback ID for argument 1 may not be zero");
1922     return successResult();
1923     }
1924    
1925     RTList<ScriptEvent>::Iterator itCallback = pEngineChannel->ScriptCallbackByID(id);
1926     if (!itCallback) return successResult(); // ignore if callback is i.e. not alive anymore
1927    
1928     const bool disableWaitForever =
1929     (args->argsCount() >= 2) ? (args->arg(1)->asInt()->evalInt() == 1) : false;
1930    
1931     pEngineChannel->ScheduleResumeOfScriptCallback(
1932 schoenebeck 3205 itCallback, m_vm->m_event->scheduleTime, disableWaitForever
1933 schoenebeck 2948 );
1934    
1935     return successResult();
1936     }
1937    
1938 schoenebeck 3277 // abort() function
1939    
1940     InstrumentScriptVMFunction_abort::InstrumentScriptVMFunction_abort(InstrumentScriptVM* parent)
1941 schoenebeck 3293 : m_vm(parent)
1942 schoenebeck 3277 {
1943     }
1944    
1945     VMFnResult* InstrumentScriptVMFunction_abort::exec(VMFnArgs* args) {
1946     const script_callback_id_t id = args->arg(0)->asInt()->evalInt();
1947     if (!id) {
1948     wrnMsg("abort(): callback ID for argument 1 may not be zero");
1949     return successResult();
1950     }
1951    
1952     AbstractEngineChannel* pEngineChannel =
1953     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1954    
1955     RTList<ScriptEvent>::Iterator itCallback = pEngineChannel->ScriptCallbackByID(id);
1956     if (!itCallback) return successResult(); // ignore if callback is i.e. not alive anymore
1957    
1958     itCallback->execCtx->signalAbort();
1959    
1960     return successResult();
1961     }
1962    
1963 schoenebeck 3293 // fork() function
1964    
1965     InstrumentScriptVMFunction_fork::InstrumentScriptVMFunction_fork(InstrumentScriptVM* parent)
1966     : m_vm(parent)
1967     {
1968     }
1969    
1970     VMFnResult* InstrumentScriptVMFunction_fork::exec(VMFnArgs* args) {
1971     // check if this is actually the parent going to fork, or rather one of
1972     // the children which is already forked
1973     if (m_vm->m_event->forkIndex != 0) { // this is the entry point for a child ...
1974     int forkResult = m_vm->m_event->forkIndex;
1975     // reset so that this child may i.e. also call fork() later on
1976     m_vm->m_event->forkIndex = 0;
1977     return successResult(forkResult);
1978     }
1979    
1980     // if we are here, then this is the parent, so we must fork this parent
1981    
1982     const int n =
1983     (args->argsCount() >= 1) ? args->arg(0)->asInt()->evalInt() : 1;
1984     const bool bAutoAbort =
1985     (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : true;
1986    
1987     if (m_vm->m_event->countChildHandlers() + n > MAX_FORK_PER_SCRIPT_HANDLER) {
1988     wrnMsg("fork(): requested amount would exceed allowed limit per event handler");
1989     return successResult(-1);
1990     }
1991    
1992     AbstractEngineChannel* pEngineChannel =
1993     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1994    
1995     if (!pEngineChannel->hasFreeScriptCallbacks(n)) {
1996     wrnMsg("fork(): global limit of event handlers exceeded");
1997     return successResult(-1);
1998     }
1999    
2000     for (int iChild = 0; iChild < n; ++iChild) {
2001     RTList<ScriptEvent>::Iterator itChild =
2002     pEngineChannel->forkScriptCallback(m_vm->m_event, bAutoAbort);
2003     if (!itChild) { // should never happen, otherwise its a bug ...
2004     errMsg("fork(): internal error while allocating child");
2005     return errorResult(-1); // terminate script
2006     }
2007     // since both parent, as well all child script execution instances
2008     // all land in this exect() method, the following is (more or less)
2009     // the only feature that lets us distinguish the parent and
2010     // respective children from each other in this exect() method
2011     itChild->forkIndex = iChild + 1;
2012     }
2013    
2014     return successResult(0);
2015     }
2016    
2017 schoenebeck 2596 } // namespace LinuxSampler

  ViewVC Help
Powered by ViewVC