/[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 2962 - (hide annotations) (download)
Sun Jul 17 17:54:04 2016 UTC (7 years, 9 months ago) by schoenebeck
File size: 47969 byte(s)
* NKSP: Fixed all change_*() built-in script functions to apply their
  synthesis parameter changes immediately in case the respective note
  was triggered at the same time, instead of scheduling the parameter
  change, especially because it would cause some parameter types's
  changes either to be ramped (i.e. change_vol()) or other types even
  to have not effect at all (i.e. change_attack()).
* Bumped version (2.0.0.svn20).

1 schoenebeck 2596 /*
2 schoenebeck 2871 * Copyright (c) 2014-2016 Christian Schoenebeck
3 schoenebeck 2596 *
4     * http://www.linuxsampler.org
5     *
6     * This file is part of LinuxSampler and released under the same terms.
7     * See README file for details.
8     */
9    
10     #include "InstrumentScriptVMFunctions.h"
11     #include "InstrumentScriptVM.h"
12     #include "../AbstractEngineChannel.h"
13    
14     namespace LinuxSampler {
15 schoenebeck 2931
16     // play_note() function
17 schoenebeck 2596
18     InstrumentScriptVMFunction_play_note::InstrumentScriptVMFunction_play_note(InstrumentScriptVM* parent)
19     : m_vm(parent)
20     {
21     }
22    
23     VMFnResult* InstrumentScriptVMFunction_play_note::exec(VMFnArgs* args) {
24     int note = args->arg(0)->asInt()->evalInt();
25     int velocity = (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : 127;
26     int sampleoffset = (args->argsCount() >= 3) ? args->arg(2)->asInt()->evalInt() : 0;
27 schoenebeck 2879 int duration = (args->argsCount() >= 4) ? args->arg(3)->asInt()->evalInt() : 0; //TODO: -1 might be a better default value instead of 0
28 schoenebeck 2596
29     if (note < 0 || note > 127) {
30     errMsg("play_note(): argument 1 is an invalid note number");
31 schoenebeck 2879 return errorResult(0);
32 schoenebeck 2596 }
33    
34     if (velocity < 0 || velocity > 127) {
35     errMsg("play_note(): argument 2 is an invalid velocity value");
36 schoenebeck 2879 return errorResult(0);
37 schoenebeck 2596 }
38    
39     if (sampleoffset < 0) {
40     errMsg("play_note(): argument 3 may not be a negative sample offset");
41 schoenebeck 2879 return errorResult(0);
42 schoenebeck 2596 } else if (sampleoffset != 0) {
43     wrnMsg("play_note(): argument 3 does not support a sample offset other than 0 yet");
44     }
45    
46     if (duration < -1) {
47     errMsg("play_note(): argument 4 must be a duration value of at least -1 or higher");
48 schoenebeck 2879 return errorResult(0);
49 schoenebeck 2596 }
50    
51     AbstractEngineChannel* pEngineChannel =
52     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
53    
54 schoenebeck 2879 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
55     e.Init(); // clear IDs
56 schoenebeck 2938 e.Type = Event::type_play_note;
57 schoenebeck 2596 e.Param.Note.Key = note;
58     e.Param.Note.Velocity = velocity;
59 schoenebeck 2879 // make this new note dependent to the life time of the original note
60     if (duration == -1) {
61     if (m_vm->currentVMEventHandler()->eventHandlerType() != VM_EVENT_HANDLER_NOTE) {
62     errMsg("play_note(): -1 for argument 4 may only be used for note event handlers");
63     return errorResult(0);
64     }
65     e.Param.Note.ParentNoteID = m_vm->m_event->cause.Param.Note.ID;
66     }
67 schoenebeck 2596
68 schoenebeck 2879 const note_id_t id = pEngineChannel->ScheduleNoteMicroSec(&e, 0);
69 schoenebeck 2596
70 schoenebeck 2938 // if a duration is supplied (and play-note event was scheduled
71     // successfully above), then schedule a subsequent stop-note event
72 schoenebeck 2879 if (id && duration > 0) {
73 schoenebeck 2938 e.Type = Event::type_stop_note;
74     e.Param.Note.ID = id;
75 schoenebeck 2871 e.Param.Note.Velocity = 127;
76     pEngineChannel->ScheduleEventMicroSec(&e, duration);
77     }
78    
79 schoenebeck 2879 // even if id is null, don't return an errorResult() here, because that
80     // would abort the script, and under heavy load it may be considerable
81     // that ScheduleNoteMicroSec() fails above, so simply ignore that
82     return successResult( ScriptID::fromNoteID(id) );
83 schoenebeck 2598 }
84    
85 schoenebeck 2931 // set_controller() function
86    
87 schoenebeck 2600 InstrumentScriptVMFunction_set_controller::InstrumentScriptVMFunction_set_controller(InstrumentScriptVM* parent)
88     : m_vm(parent)
89     {
90     }
91    
92     VMFnResult* InstrumentScriptVMFunction_set_controller::exec(VMFnArgs* args) {
93     int controller = args->arg(0)->asInt()->evalInt();
94     int value = args->arg(1)->asInt()->evalInt();
95    
96     AbstractEngineChannel* pEngineChannel =
97     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
98    
99 schoenebeck 2879 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
100     e.Init(); // clear IDs
101 schoenebeck 2600 if (controller == CTRL_TABLE_IDX_AFTERTOUCH) {
102     e.Type = Event::type_channel_pressure;
103     e.Param.ChannelPressure.Value = value & 127;
104     } else if (controller == CTRL_TABLE_IDX_PITCHBEND) {
105     e.Type = Event::type_pitchbend;
106     e.Param.Pitch.Pitch = value;
107     } else if (controller >= 0 && controller <= 127) {
108     e.Type = Event::type_control_change;
109     e.Param.CC.Controller = controller;
110     e.Param.CC.Value = value;
111     } else {
112     errMsg("set_controller(): argument 1 is an invalid controller");
113     return errorResult();
114     }
115    
116 schoenebeck 2879 const event_id_t id = pEngineChannel->ScheduleEventMicroSec(&e, 0);
117 schoenebeck 2600
118 schoenebeck 2879 // even if id is null, don't return an errorResult() here, because that
119     // would abort the script, and under heavy load it may be considerable
120     // that ScheduleEventMicroSec() fails above, so simply ignore that
121     return successResult( ScriptID::fromEventID(id) );
122 schoenebeck 2931 }
123 schoenebeck 2600
124 schoenebeck 2931 // ignore_event() function
125    
126 schoenebeck 2598 InstrumentScriptVMFunction_ignore_event::InstrumentScriptVMFunction_ignore_event(InstrumentScriptVM* parent)
127     : m_vm(parent)
128     {
129     }
130    
131 schoenebeck 2630 bool InstrumentScriptVMFunction_ignore_event::acceptsArgType(int iArg, ExprType_t type) const {
132     return type == INT_EXPR || type == INT_ARR_EXPR;
133     }
134    
135 schoenebeck 2598 VMFnResult* InstrumentScriptVMFunction_ignore_event::exec(VMFnArgs* args) {
136     AbstractEngineChannel* pEngineChannel =
137 schoenebeck 2630 static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
138 schoenebeck 2598
139 schoenebeck 2630 if (args->arg(0)->exprType() == INT_EXPR) {
140 schoenebeck 2879 const ScriptID id = args->arg(0)->asInt()->evalInt();
141     if (!id) {
142     wrnMsg("ignore_event(): event ID argument may not be zero");
143     // not errorResult(), because that would abort the script, not intentional in this case
144 schoenebeck 2630 return successResult();
145     }
146 schoenebeck 2879 pEngineChannel->IgnoreEventByScriptID(id);
147 schoenebeck 2630 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
148     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
149     for (int i = 0; i < ids->arraySize(); ++i) {
150 schoenebeck 2879 const ScriptID id = ids->evalIntElement(i);
151     pEngineChannel->IgnoreEventByScriptID(id);
152 schoenebeck 2630 }
153     }
154 schoenebeck 2598
155 schoenebeck 2596 return successResult();
156     }
157    
158 schoenebeck 2931 // ignore_controller() function
159    
160 schoenebeck 2598 InstrumentScriptVMFunction_ignore_controller::InstrumentScriptVMFunction_ignore_controller(InstrumentScriptVM* parent)
161     : m_vm(parent)
162     {
163     }
164    
165     VMFnResult* InstrumentScriptVMFunction_ignore_controller::exec(VMFnArgs* args) {
166 schoenebeck 2879 const ScriptID id = (args->argsCount() >= 1) ? args->arg(0)->asInt()->evalInt() : m_vm->m_event->id;
167     if (!id && args->argsCount() >= 1) {
168     wrnMsg("ignore_controller(): event ID argument may not be zero");
169 schoenebeck 2598 return successResult();
170     }
171    
172     AbstractEngineChannel* pEngineChannel =
173     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
174    
175 schoenebeck 2879 pEngineChannel->IgnoreEventByScriptID(id);
176 schoenebeck 2598
177     return successResult();
178     }
179    
180 schoenebeck 2931 // note_off() function
181    
182 schoenebeck 2629 InstrumentScriptVMFunction_note_off::InstrumentScriptVMFunction_note_off(InstrumentScriptVM* parent)
183     : m_vm(parent)
184     {
185     }
186    
187 schoenebeck 2630 bool InstrumentScriptVMFunction_note_off::acceptsArgType(int iArg, ExprType_t type) const {
188     return type == INT_EXPR || type == INT_ARR_EXPR;
189     }
190    
191 schoenebeck 2629 VMFnResult* InstrumentScriptVMFunction_note_off::exec(VMFnArgs* args) {
192 schoenebeck 2630 AbstractEngineChannel* pEngineChannel =
193     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
194    
195 schoenebeck 2629 int velocity = (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : 127;
196 schoenebeck 2630 if (velocity < 0 || velocity > 127) {
197     errMsg("note_off(): argument 2 is an invalid velocity value");
198     return errorResult();
199     }
200 schoenebeck 2629
201 schoenebeck 2630 if (args->arg(0)->exprType() == INT_EXPR) {
202 schoenebeck 2879 const ScriptID id = args->arg(0)->asInt()->evalInt();
203     if (!id) {
204     wrnMsg("note_off(): note ID for argument 1 may not be zero");
205 schoenebeck 2630 return successResult();
206     }
207 schoenebeck 2879 if (!id.isNoteID()) {
208     wrnMsg("note_off(): argument 1 is not a note ID");
209     return successResult();
210     }
211 schoenebeck 2630
212 schoenebeck 2879 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
213     if (!pNote) return successResult();
214 schoenebeck 2630
215 schoenebeck 2879 Event e = pNote->cause;
216     e.Init(); // clear IDs
217     e.CopyTimeFrom(m_vm->m_event->cause); // set fragment time for "now"
218 schoenebeck 2938 e.Type = Event::type_stop_note;
219     e.Param.Note.ID = id.noteID();
220     e.Param.Note.Key = pNote->hostKey;
221 schoenebeck 2630 e.Param.Note.Velocity = velocity;
222    
223 schoenebeck 2879 pEngineChannel->ScheduleEventMicroSec(&e, 0);
224 schoenebeck 2630 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
225     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
226     for (int i = 0; i < ids->arraySize(); ++i) {
227 schoenebeck 2879 const ScriptID id = ids->evalIntElement(i);
228     if (!id || !id.isNoteID()) continue;
229 schoenebeck 2630
230 schoenebeck 2879 NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
231     if (!pNote) continue;
232 schoenebeck 2630
233 schoenebeck 2879 Event e = pNote->cause;
234     e.Init(); // clear IDs
235     e.CopyTimeFrom(m_vm->m_event->cause); // set fragment time for "now"
236 schoenebeck 2938 e.Type = Event::type_stop_note;
237     e.Param.Note.ID = id.noteID();
238     e.Param.Note.Key = pNote->hostKey;
239 schoenebeck 2630 e.Param.Note.Velocity = velocity;
240    
241 schoenebeck 2879 pEngineChannel->ScheduleEventMicroSec(&e, 0);
242 schoenebeck 2630 }
243 schoenebeck 2629 }
244    
245 schoenebeck 2630 return successResult();
246     }
247    
248 schoenebeck 2931 // set_event_mark() function
249    
250 schoenebeck 2630 InstrumentScriptVMFunction_set_event_mark::InstrumentScriptVMFunction_set_event_mark(InstrumentScriptVM* parent)
251     : m_vm(parent)
252     {
253     }
254    
255     VMFnResult* InstrumentScriptVMFunction_set_event_mark::exec(VMFnArgs* args) {
256 schoenebeck 2879 const ScriptID id = args->arg(0)->asInt()->evalInt();
257     const int groupID = args->arg(1)->asInt()->evalInt();
258 schoenebeck 2630
259     if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) {
260     errMsg("set_event_mark(): argument 2 is an invalid group id");
261 schoenebeck 2629 return errorResult();
262     }
263    
264     AbstractEngineChannel* pEngineChannel =
265     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
266    
267 schoenebeck 2879 // check if the event/note still exists
268     switch (id.type()) {
269     case ScriptID::EVENT: {
270     RTList<Event>::Iterator itEvent = pEngineChannel->pEngine->EventByID( id.eventID() );
271     if (!itEvent) return successResult();
272     break;
273     }
274     case ScriptID::NOTE: {
275     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
276     if (!pNote) return successResult();
277     break;
278     }
279     }
280 schoenebeck 2629
281 schoenebeck 2879 pEngineChannel->pScript->eventGroups[groupID].insert(id);
282 schoenebeck 2629
283 schoenebeck 2630 return successResult();
284     }
285 schoenebeck 2629
286 schoenebeck 2931 // delete_event_mark() function
287    
288 schoenebeck 2630 InstrumentScriptVMFunction_delete_event_mark::InstrumentScriptVMFunction_delete_event_mark(InstrumentScriptVM* parent)
289     : m_vm(parent)
290     {
291     }
292    
293     VMFnResult* InstrumentScriptVMFunction_delete_event_mark::exec(VMFnArgs* args) {
294 schoenebeck 2879 const ScriptID id = args->arg(0)->asInt()->evalInt();
295     const int groupID = args->arg(1)->asInt()->evalInt();
296 schoenebeck 2630
297     if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) {
298     errMsg("delete_event_mark(): argument 2 is an invalid group id");
299     return errorResult();
300     }
301    
302     AbstractEngineChannel* pEngineChannel =
303     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
304    
305 schoenebeck 2879 pEngineChannel->pScript->eventGroups[groupID].erase(id);
306 schoenebeck 2630
307 schoenebeck 2629 return successResult();
308     }
309    
310 schoenebeck 2931 // by_marks() function
311    
312 schoenebeck 2630 InstrumentScriptVMFunction_by_marks::InstrumentScriptVMFunction_by_marks(InstrumentScriptVM* parent)
313     : m_vm(parent)
314     {
315     }
316    
317     int InstrumentScriptVMFunction_by_marks::Result::arraySize() const {
318     return eventGroup->size();
319     }
320    
321     int InstrumentScriptVMFunction_by_marks::Result::evalIntElement(uint i) {
322     return (*eventGroup)[i];
323     }
324    
325     VMFnResult* InstrumentScriptVMFunction_by_marks::errorResult() {
326     m_result.eventGroup = NULL;
327     m_result.flags = StmtFlags_t(STMT_ABORT_SIGNALLED | STMT_ERROR_OCCURRED);
328     return &m_result;
329     }
330    
331     VMFnResult* InstrumentScriptVMFunction_by_marks::successResult(EventGroup* eventGroup) {
332     m_result.eventGroup = eventGroup;
333     m_result.flags = STMT_SUCCESS;
334     return &m_result;
335     }
336    
337     VMFnResult* InstrumentScriptVMFunction_by_marks::exec(VMFnArgs* args) {
338     int groupID = args->arg(0)->asInt()->evalInt();
339    
340     if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) {
341     errMsg("by_marks(): argument is an invalid group id");
342     return errorResult();
343     }
344    
345     AbstractEngineChannel* pEngineChannel =
346     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
347    
348     return successResult( &pEngineChannel->pScript->eventGroups[groupID] );
349     }
350    
351 schoenebeck 2931 // change_vol() function
352    
353     InstrumentScriptVMFunction_change_vol::InstrumentScriptVMFunction_change_vol(InstrumentScriptVM* parent)
354     : m_vm(parent)
355     {
356     }
357    
358     bool InstrumentScriptVMFunction_change_vol::acceptsArgType(int iArg, ExprType_t type) const {
359     if (iArg == 0)
360     return type == INT_EXPR || type == INT_ARR_EXPR;
361     else
362     return INT_EXPR;
363     }
364    
365     VMFnResult* InstrumentScriptVMFunction_change_vol::exec(VMFnArgs* args) {
366     int volume = args->arg(1)->asInt()->evalInt(); // volume change in milli dB
367     bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;
368 schoenebeck 2962 const float fVolumeLin = RTMath::DecibelToLinRatio(float(volume) / 1000.f);
369 schoenebeck 2931
370     AbstractEngineChannel* pEngineChannel =
371     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
372    
373     if (args->arg(0)->exprType() == INT_EXPR) {
374     const ScriptID id = args->arg(0)->asInt()->evalInt();
375     if (!id) {
376     wrnMsg("change_vol(): note ID for argument 1 may not be zero");
377     return successResult();
378     }
379     if (!id.isNoteID()) {
380     wrnMsg("change_vol(): argument 1 is not a note ID");
381     return successResult();
382     }
383    
384     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
385     if (!pNote) return successResult();
386    
387 schoenebeck 2962 // if change_vol() was called immediately after note was triggered
388     // then immediately apply the volume to note object
389     if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
390     if (relative)
391 schoenebeck 2931 pNote->Override.Volume *= fVolumeLin;
392     else
393 schoenebeck 2962 pNote->Override.Volume = fVolumeLin;
394     } else { // otherwise schedule the volume change ...
395 schoenebeck 2931 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
396     e.Init(); // clear IDs
397     e.Type = Event::type_note_synth_param;
398     e.Param.NoteSynthParam.NoteID = id.noteID();
399     e.Param.NoteSynthParam.Type = Event::synth_param_volume;
400     e.Param.NoteSynthParam.Delta = fVolumeLin;
401     e.Param.NoteSynthParam.Relative = relative;
402    
403     pEngineChannel->ScheduleEventMicroSec(&e, 0);
404     }
405 schoenebeck 2962 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
406     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
407     for (int i = 0; i < ids->arraySize(); ++i) {
408     const ScriptID id = ids->evalIntElement(i);
409     if (!id || !id.isNoteID()) continue;
410    
411     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
412     if (!pNote) continue;
413    
414     // if change_vol() was called immediately after note was triggered
415     // then immediately apply the volume to Note object
416     if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
417     if (relative)
418     pNote->Override.Volume *= fVolumeLin;
419     else
420     pNote->Override.Volume = fVolumeLin;
421     } else { // otherwise schedule the volume change ...
422     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
423     e.Init(); // clear IDs
424     e.Type = Event::type_note_synth_param;
425     e.Param.NoteSynthParam.NoteID = id.noteID();
426     e.Param.NoteSynthParam.Type = Event::synth_param_volume;
427     e.Param.NoteSynthParam.Delta = fVolumeLin;
428     e.Param.NoteSynthParam.Relative = relative;
429    
430     pEngineChannel->ScheduleEventMicroSec(&e, 0);
431     }
432     }
433 schoenebeck 2931 }
434    
435     return successResult();
436     }
437    
438     // change_tune() function
439    
440     InstrumentScriptVMFunction_change_tune::InstrumentScriptVMFunction_change_tune(InstrumentScriptVM* parent)
441     : m_vm(parent)
442     {
443     }
444    
445     bool InstrumentScriptVMFunction_change_tune::acceptsArgType(int iArg, ExprType_t type) const {
446     if (iArg == 0)
447     return type == INT_EXPR || type == INT_ARR_EXPR;
448     else
449     return INT_EXPR;
450     }
451    
452     VMFnResult* InstrumentScriptVMFunction_change_tune::exec(VMFnArgs* args) {
453     int tune = args->arg(1)->asInt()->evalInt(); // tuning change in milli cents
454     bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;
455 schoenebeck 2962 const float fFreqRatio = RTMath::CentsToFreqRatioUnlimited(float(tune) / 1000.f);
456 schoenebeck 2931
457     AbstractEngineChannel* pEngineChannel =
458     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
459    
460     if (args->arg(0)->exprType() == INT_EXPR) {
461     const ScriptID id = args->arg(0)->asInt()->evalInt();
462     if (!id) {
463     wrnMsg("change_tune(): note ID for argument 1 may not be zero");
464     return successResult();
465     }
466     if (!id.isNoteID()) {
467     wrnMsg("change_tune(): argument 1 is not a note ID");
468     return successResult();
469     }
470    
471     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
472     if (!pNote) return successResult();
473    
474 schoenebeck 2962 // if change_tune() was called immediately after note was triggered
475     // then immediately apply the tuning to Note object
476     if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
477     if (relative)
478 schoenebeck 2931 pNote->Override.Pitch *= fFreqRatio;
479     else
480 schoenebeck 2962 pNote->Override.Pitch = fFreqRatio;
481     } else { // otherwise schedule tuning change ...
482 schoenebeck 2931 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
483     e.Init(); // clear IDs
484     e.Type = Event::type_note_synth_param;
485     e.Param.NoteSynthParam.NoteID = id.noteID();
486     e.Param.NoteSynthParam.Type = Event::synth_param_pitch;
487     e.Param.NoteSynthParam.Delta = fFreqRatio;
488     e.Param.NoteSynthParam.Relative = relative;
489    
490     pEngineChannel->ScheduleEventMicroSec(&e, 0);
491     }
492 schoenebeck 2962 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
493     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
494     for (int i = 0; i < ids->arraySize(); ++i) {
495     const ScriptID id = ids->evalIntElement(i);
496     if (!id || !id.isNoteID()) continue;
497    
498     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
499     if (!pNote) continue;
500    
501     // if change_tune() was called immediately after note was triggered
502     // then immediately apply the tuning to Note object
503     if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
504     if (relative)
505     pNote->Override.Pitch *= fFreqRatio;
506     else
507     pNote->Override.Pitch = fFreqRatio;
508     } else { // otherwise schedule tuning change ...
509     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
510     e.Init(); // clear IDs
511     e.Type = Event::type_note_synth_param;
512     e.Param.NoteSynthParam.NoteID = id.noteID();
513     e.Param.NoteSynthParam.Type = Event::synth_param_pitch;
514     e.Param.NoteSynthParam.Delta = fFreqRatio;
515     e.Param.NoteSynthParam.Relative = relative;
516    
517     pEngineChannel->ScheduleEventMicroSec(&e, 0);
518     }
519     }
520 schoenebeck 2931 }
521    
522     return successResult();
523     }
524    
525     // change_pan() function
526    
527     InstrumentScriptVMFunction_change_pan::InstrumentScriptVMFunction_change_pan(InstrumentScriptVM* parent)
528     : m_vm(parent)
529     {
530     }
531    
532     bool InstrumentScriptVMFunction_change_pan::acceptsArgType(int iArg, ExprType_t type) const {
533     if (iArg == 0)
534     return type == INT_EXPR || type == INT_ARR_EXPR;
535     else
536     return INT_EXPR;
537     }
538    
539     VMFnResult* InstrumentScriptVMFunction_change_pan::exec(VMFnArgs* args) {
540     int pan = args->arg(1)->asInt()->evalInt();
541     bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false;
542    
543     if (pan > 1000) {
544     wrnMsg("change_pan(): argument 2 may not be larger than 1000");
545     pan = 1000;
546     } else if (pan < -1000) {
547     wrnMsg("change_pan(): argument 2 may not be smaller than -1000");
548     pan = -1000;
549     }
550 schoenebeck 2962 const float fPan = float(pan) / 1000.f;
551 schoenebeck 2931
552     AbstractEngineChannel* pEngineChannel =
553     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
554    
555     if (args->arg(0)->exprType() == INT_EXPR) {
556     const ScriptID id = args->arg(0)->asInt()->evalInt();
557     if (!id) {
558     wrnMsg("change_pan(): note ID for argument 1 may not be zero");
559     return successResult();
560     }
561     if (!id.isNoteID()) {
562     wrnMsg("change_pan(): argument 1 is not a note ID");
563     return successResult();
564     }
565    
566     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
567     if (!pNote) return successResult();
568    
569 schoenebeck 2962 // if change_pan() was called immediately after note was triggered
570     // then immediately apply the panning to Note object
571     if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
572     if (relative) {
573 schoenebeck 2931 pNote->Override.Pan = RTMath::RelativeSummedAvg(pNote->Override.Pan, fPan, ++pNote->Override.PanSources);
574     } else {
575     pNote->Override.Pan = fPan;
576     pNote->Override.PanSources = 1; // only relevant on subsequent change_pan() calls on same note with 'relative' being set
577 schoenebeck 2962 }
578     } else { // otherwise schedule panning change ...
579 schoenebeck 2931 Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
580     e.Init(); // clear IDs
581     e.Type = Event::type_note_synth_param;
582     e.Param.NoteSynthParam.NoteID = id.noteID();
583     e.Param.NoteSynthParam.Type = Event::synth_param_pan;
584     e.Param.NoteSynthParam.Delta = fPan;
585     e.Param.NoteSynthParam.Relative = relative;
586    
587     pEngineChannel->ScheduleEventMicroSec(&e, 0);
588     }
589 schoenebeck 2962 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
590     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
591     for (int i = 0; i < ids->arraySize(); ++i) {
592     const ScriptID id = ids->evalIntElement(i);
593     if (!id || !id.isNoteID()) continue;
594    
595     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
596     if (!pNote) continue;
597    
598     // if change_pan() was called immediately after note was triggered
599     // then immediately apply the panning to Note object
600     if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
601     if (relative) {
602     pNote->Override.Pan = RTMath::RelativeSummedAvg(pNote->Override.Pan, fPan, ++pNote->Override.PanSources);
603     } else {
604     pNote->Override.Pan = fPan;
605     pNote->Override.PanSources = 1; // only relevant on subsequent change_pan() calls on same note with 'relative' being set
606     }
607     } else { // otherwise schedule panning change ...
608     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
609     e.Init(); // clear IDs
610     e.Type = Event::type_note_synth_param;
611     e.Param.NoteSynthParam.NoteID = id.noteID();
612     e.Param.NoteSynthParam.Type = Event::synth_param_pan;
613     e.Param.NoteSynthParam.Delta = fPan;
614     e.Param.NoteSynthParam.Relative = relative;
615    
616     pEngineChannel->ScheduleEventMicroSec(&e, 0);
617     }
618     }
619 schoenebeck 2931 }
620    
621     return successResult();
622     }
623    
624 schoenebeck 2935 #define VM_FILTER_PAR_MAX_VALUE 1000000
625 schoenebeck 2953 #define VM_EG_PAR_MAX_VALUE 1000000
626 schoenebeck 2935
627     // change_cutoff() function
628    
629     InstrumentScriptVMFunction_change_cutoff::InstrumentScriptVMFunction_change_cutoff(InstrumentScriptVM* parent)
630     : m_vm(parent)
631     {
632     }
633    
634     bool InstrumentScriptVMFunction_change_cutoff::acceptsArgType(int iArg, ExprType_t type) const {
635     if (iArg == 0)
636     return type == INT_EXPR || type == INT_ARR_EXPR;
637     else
638     return INT_EXPR;
639     }
640    
641     VMFnResult* InstrumentScriptVMFunction_change_cutoff::exec(VMFnArgs* args) {
642     int cutoff = args->arg(1)->asInt()->evalInt();
643     if (cutoff > VM_FILTER_PAR_MAX_VALUE) {
644     wrnMsg("change_cutoff(): argument 2 may not be larger than 1000000");
645     cutoff = VM_FILTER_PAR_MAX_VALUE;
646     } else if (cutoff < 0) {
647     wrnMsg("change_cutoff(): argument 2 may not be negative");
648     cutoff = 0;
649     }
650 schoenebeck 2962 const float fCutoff = float(cutoff) / float(VM_FILTER_PAR_MAX_VALUE);
651 schoenebeck 2935
652     AbstractEngineChannel* pEngineChannel =
653     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
654    
655     if (args->arg(0)->exprType() == INT_EXPR) {
656     const ScriptID id = args->arg(0)->asInt()->evalInt();
657     if (!id) {
658     wrnMsg("change_cutoff(): note ID for argument 1 may not be zero");
659     return successResult();
660     }
661     if (!id.isNoteID()) {
662     wrnMsg("change_cutoff(): argument 1 is not a note ID");
663     return successResult();
664     }
665    
666     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
667     if (!pNote) return successResult();
668    
669 schoenebeck 2962 // if change_cutoff() was called immediately after note was triggered
670     // then immediately apply cutoff to Note object
671     if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
672     pNote->Override.Cutoff = fCutoff;
673     } else { // otherwise schedule cutoff change ...
674     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
675     e.Init(); // clear IDs
676     e.Type = Event::type_note_synth_param;
677     e.Param.NoteSynthParam.NoteID = id.noteID();
678     e.Param.NoteSynthParam.Type = Event::synth_param_cutoff;
679     e.Param.NoteSynthParam.Delta = fCutoff;
680     e.Param.NoteSynthParam.Relative = false;
681 schoenebeck 2935
682 schoenebeck 2962 pEngineChannel->ScheduleEventMicroSec(&e, 0);
683     }
684 schoenebeck 2935 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
685     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
686     for (int i = 0; i < ids->arraySize(); ++i) {
687     const ScriptID id = ids->evalIntElement(i);
688     if (!id || !id.isNoteID()) continue;
689    
690     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
691     if (!pNote) continue;
692    
693 schoenebeck 2962 // if change_cutoff() was called immediately after note was triggered
694     // then immediately apply cutoff to Note object
695     if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
696     pNote->Override.Cutoff = fCutoff;
697     } else { // otherwise schedule cutoff change ...
698     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
699     e.Init(); // clear IDs
700     e.Type = Event::type_note_synth_param;
701     e.Param.NoteSynthParam.NoteID = id.noteID();
702     e.Param.NoteSynthParam.Type = Event::synth_param_cutoff;
703     e.Param.NoteSynthParam.Delta = fCutoff;
704     e.Param.NoteSynthParam.Relative = false;
705 schoenebeck 2935
706 schoenebeck 2962 pEngineChannel->ScheduleEventMicroSec(&e, 0);
707     }
708 schoenebeck 2935 }
709     }
710    
711     return successResult();
712     }
713    
714     // change_reso() function
715    
716     InstrumentScriptVMFunction_change_reso::InstrumentScriptVMFunction_change_reso(InstrumentScriptVM* parent)
717     : m_vm(parent)
718     {
719     }
720    
721     bool InstrumentScriptVMFunction_change_reso::acceptsArgType(int iArg, ExprType_t type) const {
722     if (iArg == 0)
723     return type == INT_EXPR || type == INT_ARR_EXPR;
724     else
725     return INT_EXPR;
726     }
727    
728     VMFnResult* InstrumentScriptVMFunction_change_reso::exec(VMFnArgs* args) {
729     int resonance = args->arg(1)->asInt()->evalInt();
730     if (resonance > VM_FILTER_PAR_MAX_VALUE) {
731     wrnMsg("change_reso(): argument 2 may not be larger than 1000000");
732     resonance = VM_FILTER_PAR_MAX_VALUE;
733     } else if (resonance < 0) {
734     wrnMsg("change_reso(): argument 2 may not be negative");
735     resonance = 0;
736     }
737 schoenebeck 2962 const float fResonance = float(resonance) / float(VM_FILTER_PAR_MAX_VALUE);
738 schoenebeck 2935
739     AbstractEngineChannel* pEngineChannel =
740     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
741    
742     if (args->arg(0)->exprType() == INT_EXPR) {
743     const ScriptID id = args->arg(0)->asInt()->evalInt();
744     if (!id) {
745     wrnMsg("change_reso(): note ID for argument 1 may not be zero");
746     return successResult();
747     }
748     if (!id.isNoteID()) {
749     wrnMsg("change_reso(): argument 1 is not a note ID");
750     return successResult();
751     }
752    
753     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
754     if (!pNote) return successResult();
755    
756 schoenebeck 2962 // if change_reso() was called immediately after note was triggered
757     // then immediately apply resonance to Note object
758     if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
759     pNote->Override.Resonance = fResonance;
760     } else { // otherwise schedule resonance change ...
761     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
762     e.Init(); // clear IDs
763     e.Type = Event::type_note_synth_param;
764     e.Param.NoteSynthParam.NoteID = id.noteID();
765     e.Param.NoteSynthParam.Type = Event::synth_param_resonance;
766     e.Param.NoteSynthParam.Delta = fResonance;
767     e.Param.NoteSynthParam.Relative = false;
768 schoenebeck 2935
769 schoenebeck 2962 pEngineChannel->ScheduleEventMicroSec(&e, 0);
770     }
771 schoenebeck 2935 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
772     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
773     for (int i = 0; i < ids->arraySize(); ++i) {
774     const ScriptID id = ids->evalIntElement(i);
775     if (!id || !id.isNoteID()) continue;
776    
777     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
778     if (!pNote) continue;
779    
780 schoenebeck 2962 // if change_reso() was called immediately after note was triggered
781     // then immediately apply resonance to Note object
782     if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
783     pNote->Override.Resonance = fResonance;
784     } else { // otherwise schedule resonance change ...
785     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
786     e.Init(); // clear IDs
787     e.Type = Event::type_note_synth_param;
788     e.Param.NoteSynthParam.NoteID = id.noteID();
789     e.Param.NoteSynthParam.Type = Event::synth_param_resonance;
790     e.Param.NoteSynthParam.Delta = fResonance;
791     e.Param.NoteSynthParam.Relative = false;
792 schoenebeck 2935
793 schoenebeck 2962 pEngineChannel->ScheduleEventMicroSec(&e, 0);
794     }
795 schoenebeck 2935 }
796     }
797    
798     return successResult();
799     }
800 schoenebeck 2953
801     // change_attack() function
802 schoenebeck 2935
803 schoenebeck 2953 InstrumentScriptVMFunction_change_attack::InstrumentScriptVMFunction_change_attack(InstrumentScriptVM* parent)
804     : m_vm(parent)
805     {
806     }
807    
808     bool InstrumentScriptVMFunction_change_attack::acceptsArgType(int iArg, ExprType_t type) const {
809     if (iArg == 0)
810     return type == INT_EXPR || type == INT_ARR_EXPR;
811     else
812     return INT_EXPR;
813     }
814    
815     VMFnResult* InstrumentScriptVMFunction_change_attack::exec(VMFnArgs* args) {
816     int attack = args->arg(1)->asInt()->evalInt();
817     if (attack > VM_EG_PAR_MAX_VALUE) {
818     wrnMsg("change_attack(): argument 2 may not be larger than 1000000");
819     attack = VM_EG_PAR_MAX_VALUE;
820     } else if (attack < 0) {
821     wrnMsg("change_attack(): argument 2 may not be negative");
822     attack = 0;
823     }
824     const float fAttack = float(attack) / float(VM_EG_PAR_MAX_VALUE);
825    
826     AbstractEngineChannel* pEngineChannel =
827     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
828    
829     if (args->arg(0)->exprType() == INT_EXPR) {
830     const ScriptID id = args->arg(0)->asInt()->evalInt();
831     if (!id) {
832     wrnMsg("change_attack(): note ID for argument 1 may not be zero");
833     return successResult();
834     }
835     if (!id.isNoteID()) {
836     wrnMsg("change_attack(): argument 1 is not a note ID");
837     return successResult();
838     }
839    
840     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
841 schoenebeck 2962 if (!pNote) return successResult();
842 schoenebeck 2953
843 schoenebeck 2962 // if change_attack() was called immediately after note was triggered
844     // then immediately apply attack to Note object
845     if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
846     pNote->Override.Attack = fAttack;
847     } else { // otherwise schedule attack change ...
848     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
849     e.Init(); // clear IDs
850     e.Type = Event::type_note_synth_param;
851     e.Param.NoteSynthParam.NoteID = id.noteID();
852     e.Param.NoteSynthParam.Type = Event::synth_param_attack;
853     e.Param.NoteSynthParam.Delta = fAttack;
854     e.Param.NoteSynthParam.Relative = false;
855 schoenebeck 2953
856 schoenebeck 2962 pEngineChannel->ScheduleEventMicroSec(&e, 0);
857     }
858 schoenebeck 2953 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
859     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
860     for (int i = 0; i < ids->arraySize(); ++i) {
861     const ScriptID id = ids->evalIntElement(i);
862     if (!id || !id.isNoteID()) continue;
863    
864     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
865     if (!pNote) continue;
866    
867 schoenebeck 2962 // if change_attack() was called immediately after note was triggered
868     // then immediately apply attack to Note object
869     if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
870     pNote->Override.Attack = fAttack;
871     } else { // otherwise schedule attack change ...
872     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
873     e.Init(); // clear IDs
874     e.Type = Event::type_note_synth_param;
875     e.Param.NoteSynthParam.NoteID = id.noteID();
876     e.Param.NoteSynthParam.Type = Event::synth_param_attack;
877     e.Param.NoteSynthParam.Delta = fAttack;
878     e.Param.NoteSynthParam.Relative = false;
879 schoenebeck 2953
880 schoenebeck 2962 pEngineChannel->ScheduleEventMicroSec(&e, 0);
881     }
882 schoenebeck 2953 }
883     }
884    
885     return successResult();
886     }
887    
888     // change_decay() function
889    
890     InstrumentScriptVMFunction_change_decay::InstrumentScriptVMFunction_change_decay(InstrumentScriptVM* parent)
891     : m_vm(parent)
892     {
893     }
894    
895     bool InstrumentScriptVMFunction_change_decay::acceptsArgType(int iArg, ExprType_t type) const {
896     if (iArg == 0)
897     return type == INT_EXPR || type == INT_ARR_EXPR;
898     else
899     return INT_EXPR;
900     }
901    
902     VMFnResult* InstrumentScriptVMFunction_change_decay::exec(VMFnArgs* args) {
903     int decay = args->arg(1)->asInt()->evalInt();
904     if (decay > VM_EG_PAR_MAX_VALUE) {
905     wrnMsg("change_decay(): argument 2 may not be larger than 1000000");
906     decay = VM_EG_PAR_MAX_VALUE;
907     } else if (decay < 0) {
908     wrnMsg("change_decay(): argument 2 may not be negative");
909     decay = 0;
910     }
911     const float fDecay = float(decay) / float(VM_EG_PAR_MAX_VALUE);
912    
913     AbstractEngineChannel* pEngineChannel =
914     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
915    
916     if (args->arg(0)->exprType() == INT_EXPR) {
917     const ScriptID id = args->arg(0)->asInt()->evalInt();
918     if (!id) {
919     wrnMsg("change_decay(): note ID for argument 1 may not be zero");
920     return successResult();
921     }
922     if (!id.isNoteID()) {
923     wrnMsg("change_decay(): argument 1 is not a note ID");
924     return successResult();
925     }
926    
927     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
928 schoenebeck 2962 if (!pNote) return successResult();
929 schoenebeck 2953
930 schoenebeck 2962 // if change_decay() was called immediately after note was triggered
931     // then immediately apply decay to Note object
932     if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
933     pNote->Override.Decay = fDecay;
934     } else { // otherwise schedule decay change ...
935     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
936     e.Init(); // clear IDs
937     e.Type = Event::type_note_synth_param;
938     e.Param.NoteSynthParam.NoteID = id.noteID();
939     e.Param.NoteSynthParam.Type = Event::synth_param_decay;
940     e.Param.NoteSynthParam.Delta = fDecay;
941     e.Param.NoteSynthParam.Relative = false;
942 schoenebeck 2953
943 schoenebeck 2962 pEngineChannel->ScheduleEventMicroSec(&e, 0);
944     }
945 schoenebeck 2953 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
946     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
947     for (int i = 0; i < ids->arraySize(); ++i) {
948     const ScriptID id = ids->evalIntElement(i);
949     if (!id || !id.isNoteID()) continue;
950    
951     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
952     if (!pNote) continue;
953    
954 schoenebeck 2962 // if change_decay() was called immediately after note was triggered
955     // then immediately apply decay to Note object
956     if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
957     pNote->Override.Decay = fDecay;
958     } else { // otherwise schedule decay change ...
959     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
960     e.Init(); // clear IDs
961     e.Type = Event::type_note_synth_param;
962     e.Param.NoteSynthParam.NoteID = id.noteID();
963     e.Param.NoteSynthParam.Type = Event::synth_param_decay;
964     e.Param.NoteSynthParam.Delta = fDecay;
965     e.Param.NoteSynthParam.Relative = false;
966 schoenebeck 2953
967 schoenebeck 2962 pEngineChannel->ScheduleEventMicroSec(&e, 0);
968     }
969 schoenebeck 2953 }
970     }
971    
972     return successResult();
973     }
974    
975     // change_release() function
976    
977     InstrumentScriptVMFunction_change_release::InstrumentScriptVMFunction_change_release(InstrumentScriptVM* parent)
978     : m_vm(parent)
979     {
980     }
981    
982     bool InstrumentScriptVMFunction_change_release::acceptsArgType(int iArg, ExprType_t type) const {
983     if (iArg == 0)
984     return type == INT_EXPR || type == INT_ARR_EXPR;
985     else
986     return INT_EXPR;
987     }
988    
989     VMFnResult* InstrumentScriptVMFunction_change_release::exec(VMFnArgs* args) {
990     int release = args->arg(1)->asInt()->evalInt();
991     if (release > VM_EG_PAR_MAX_VALUE) {
992     wrnMsg("change_release(): argument 2 may not be larger than 1000000");
993     release = VM_EG_PAR_MAX_VALUE;
994     } else if (release < 0) {
995     wrnMsg("change_release(): argument 2 may not be negative");
996     release = 0;
997     }
998     const float fRelease = float(release) / float(VM_EG_PAR_MAX_VALUE);
999    
1000     AbstractEngineChannel* pEngineChannel =
1001     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1002    
1003     if (args->arg(0)->exprType() == INT_EXPR) {
1004     const ScriptID id = args->arg(0)->asInt()->evalInt();
1005     if (!id) {
1006     wrnMsg("change_release(): note ID for argument 1 may not be zero");
1007     return successResult();
1008     }
1009     if (!id.isNoteID()) {
1010     wrnMsg("change_release(): argument 1 is not a note ID");
1011     return successResult();
1012     }
1013    
1014     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1015 schoenebeck 2962 if (!pNote) return successResult();
1016 schoenebeck 2953
1017 schoenebeck 2962 // if change_release() was called immediately after note was triggered
1018     // then immediately apply relase to Note object
1019     if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
1020     pNote->Override.Release = fRelease;
1021     } else { // otherwise schedule release change ...
1022     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1023     e.Init(); // clear IDs
1024     e.Type = Event::type_note_synth_param;
1025     e.Param.NoteSynthParam.NoteID = id.noteID();
1026     e.Param.NoteSynthParam.Type = Event::synth_param_release;
1027     e.Param.NoteSynthParam.Delta = fRelease;
1028     e.Param.NoteSynthParam.Relative = false;
1029 schoenebeck 2953
1030 schoenebeck 2962 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1031     }
1032 schoenebeck 2953 } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1033     VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1034     for (int i = 0; i < ids->arraySize(); ++i) {
1035     const ScriptID id = ids->evalIntElement(i);
1036     if (!id || !id.isNoteID()) continue;
1037    
1038     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1039     if (!pNote) continue;
1040    
1041 schoenebeck 2962 // if change_release() was called immediately after note was triggered
1042     // then immediately apply relase to Note object
1043     if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
1044     pNote->Override.Release = fRelease;
1045     } else { // otherwise schedule release change ...
1046     Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1047     e.Init(); // clear IDs
1048     e.Type = Event::type_note_synth_param;
1049     e.Param.NoteSynthParam.NoteID = id.noteID();
1050     e.Param.NoteSynthParam.Type = Event::synth_param_release;
1051     e.Param.NoteSynthParam.Delta = fRelease;
1052     e.Param.NoteSynthParam.Relative = false;
1053 schoenebeck 2953
1054 schoenebeck 2962 pEngineChannel->ScheduleEventMicroSec(&e, 0);
1055     }
1056 schoenebeck 2953 }
1057     }
1058    
1059     return successResult();
1060     }
1061    
1062 schoenebeck 2935 // event_status() function
1063    
1064     InstrumentScriptVMFunction_event_status::InstrumentScriptVMFunction_event_status(InstrumentScriptVM* parent)
1065     : m_vm(parent)
1066     {
1067     }
1068    
1069     VMFnResult* InstrumentScriptVMFunction_event_status::exec(VMFnArgs* args) {
1070     AbstractEngineChannel* pEngineChannel =
1071     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1072    
1073     const ScriptID id = args->arg(0)->asInt()->evalInt();
1074     if (!id) {
1075     wrnMsg("event_status(): note ID for argument 1 may not be zero");
1076     return successResult(EVENT_STATUS_INACTIVE);
1077     }
1078     if (!id.isNoteID()) {
1079     wrnMsg("event_status(): argument 1 is not a note ID");
1080     return successResult(EVENT_STATUS_INACTIVE);
1081     }
1082    
1083     NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1084     return successResult(pNote ? EVENT_STATUS_NOTE_QUEUE : EVENT_STATUS_INACTIVE);
1085     }
1086    
1087 schoenebeck 2948 // wait() function (overrides core wait() implementation)
1088    
1089     InstrumentScriptVMFunction_wait::InstrumentScriptVMFunction_wait(InstrumentScriptVM* parent)
1090     : CoreVMFunction_wait(parent)
1091     {
1092     }
1093    
1094     VMFnResult* InstrumentScriptVMFunction_wait::exec(VMFnArgs* args) {
1095     InstrumentScriptVM* m_vm = (InstrumentScriptVM*) vm;
1096    
1097     // this might be set by passing 1 with the 2nd argument of built-in stop_wait() function
1098     if (m_vm->m_event->ignoreAllWaitCalls) return successResult();
1099    
1100     return CoreVMFunction_wait::exec(args);
1101     }
1102    
1103     // stop_wait() function
1104    
1105     InstrumentScriptVMFunction_stop_wait::InstrumentScriptVMFunction_stop_wait(InstrumentScriptVM* parent)
1106     : m_vm(parent)
1107     {
1108     }
1109    
1110     VMFnResult* InstrumentScriptVMFunction_stop_wait::exec(VMFnArgs* args) {
1111     AbstractEngineChannel* pEngineChannel =
1112     static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1113    
1114     const script_callback_id_t id = args->arg(0)->asInt()->evalInt();
1115     if (!id) {
1116     wrnMsg("stop_wait(): callback ID for argument 1 may not be zero");
1117     return successResult();
1118     }
1119    
1120     RTList<ScriptEvent>::Iterator itCallback = pEngineChannel->ScriptCallbackByID(id);
1121     if (!itCallback) return successResult(); // ignore if callback is i.e. not alive anymore
1122    
1123     const bool disableWaitForever =
1124     (args->argsCount() >= 2) ? (args->arg(1)->asInt()->evalInt() == 1) : false;
1125    
1126     pEngineChannel->ScheduleResumeOfScriptCallback(
1127     itCallback, m_vm->m_event->cause.SchedTime(), disableWaitForever
1128     );
1129    
1130     return successResult();
1131     }
1132    
1133 schoenebeck 2596 } // namespace LinuxSampler

  ViewVC Help
Powered by ViewVC