/[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 3251 - (hide annotations) (download)
Mon May 29 22:19:19 2017 UTC (6 years, 10 months ago) by schoenebeck
File size: 81245 byte(s)
* NKSP: built-in "play_note()" function now supports a sample playback
  start offset with argument 3, where special value -1 means to use the
  regular sample offset as defined by the instrument file.
* Bumped version (2.0.0.svn55).

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

  ViewVC Help
Powered by ViewVC