1 |
/* |
2 |
* Copyright (c) 2014-2016 Christian Schoenebeck |
3 |
* |
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 |
|
16 |
// play_note() function |
17 |
|
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 |
int duration = (args->argsCount() >= 4) ? args->arg(3)->asInt()->evalInt() : 0; //TODO: -1 might be a better default value instead of 0 |
28 |
|
29 |
if (note < 0 || note > 127) { |
30 |
errMsg("play_note(): argument 1 is an invalid note number"); |
31 |
return errorResult(0); |
32 |
} |
33 |
|
34 |
if (velocity < 0 || velocity > 127) { |
35 |
errMsg("play_note(): argument 2 is an invalid velocity value"); |
36 |
return errorResult(0); |
37 |
} |
38 |
|
39 |
if (sampleoffset < 0) { |
40 |
errMsg("play_note(): argument 3 may not be a negative sample offset"); |
41 |
return errorResult(0); |
42 |
} 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 |
return errorResult(0); |
49 |
} |
50 |
|
51 |
AbstractEngineChannel* pEngineChannel = |
52 |
static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel); |
53 |
|
54 |
Event e = m_vm->m_event->cause; // copy to get fragment time for "now" |
55 |
e.Init(); // clear IDs |
56 |
e.Type = Event::type_note_on; |
57 |
e.Param.Note.Key = note; |
58 |
e.Param.Note.Velocity = velocity; |
59 |
// 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 |
|
68 |
const note_id_t id = pEngineChannel->ScheduleNoteMicroSec(&e, 0); |
69 |
|
70 |
// if a duration is supplied (and note-on event was scheduled |
71 |
// successfully above), then schedule a subsequent note-off event |
72 |
if (id && duration > 0) { |
73 |
e.Type = Event::type_note_off; |
74 |
e.Param.Note.Velocity = 127; |
75 |
pEngineChannel->ScheduleEventMicroSec(&e, duration); |
76 |
} |
77 |
|
78 |
// even if id is null, don't return an errorResult() here, because that |
79 |
// would abort the script, and under heavy load it may be considerable |
80 |
// that ScheduleNoteMicroSec() fails above, so simply ignore that |
81 |
return successResult( ScriptID::fromNoteID(id) ); |
82 |
} |
83 |
|
84 |
// set_controller() function |
85 |
|
86 |
InstrumentScriptVMFunction_set_controller::InstrumentScriptVMFunction_set_controller(InstrumentScriptVM* parent) |
87 |
: m_vm(parent) |
88 |
{ |
89 |
} |
90 |
|
91 |
VMFnResult* InstrumentScriptVMFunction_set_controller::exec(VMFnArgs* args) { |
92 |
int controller = args->arg(0)->asInt()->evalInt(); |
93 |
int value = args->arg(1)->asInt()->evalInt(); |
94 |
|
95 |
AbstractEngineChannel* pEngineChannel = |
96 |
static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel); |
97 |
|
98 |
Event e = m_vm->m_event->cause; // copy to get fragment time for "now" |
99 |
e.Init(); // clear IDs |
100 |
if (controller == CTRL_TABLE_IDX_AFTERTOUCH) { |
101 |
e.Type = Event::type_channel_pressure; |
102 |
e.Param.ChannelPressure.Value = value & 127; |
103 |
} else if (controller == CTRL_TABLE_IDX_PITCHBEND) { |
104 |
e.Type = Event::type_pitchbend; |
105 |
e.Param.Pitch.Pitch = value; |
106 |
} else if (controller >= 0 && controller <= 127) { |
107 |
e.Type = Event::type_control_change; |
108 |
e.Param.CC.Controller = controller; |
109 |
e.Param.CC.Value = value; |
110 |
} else { |
111 |
errMsg("set_controller(): argument 1 is an invalid controller"); |
112 |
return errorResult(); |
113 |
} |
114 |
|
115 |
const event_id_t id = pEngineChannel->ScheduleEventMicroSec(&e, 0); |
116 |
|
117 |
// even if id is null, don't return an errorResult() here, because that |
118 |
// would abort the script, and under heavy load it may be considerable |
119 |
// that ScheduleEventMicroSec() fails above, so simply ignore that |
120 |
return successResult( ScriptID::fromEventID(id) ); |
121 |
} |
122 |
|
123 |
// ignore_event() function |
124 |
|
125 |
InstrumentScriptVMFunction_ignore_event::InstrumentScriptVMFunction_ignore_event(InstrumentScriptVM* parent) |
126 |
: m_vm(parent) |
127 |
{ |
128 |
} |
129 |
|
130 |
bool InstrumentScriptVMFunction_ignore_event::acceptsArgType(int iArg, ExprType_t type) const { |
131 |
return type == INT_EXPR || type == INT_ARR_EXPR; |
132 |
} |
133 |
|
134 |
VMFnResult* InstrumentScriptVMFunction_ignore_event::exec(VMFnArgs* args) { |
135 |
AbstractEngineChannel* pEngineChannel = |
136 |
static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel); |
137 |
|
138 |
if (args->arg(0)->exprType() == INT_EXPR) { |
139 |
const ScriptID id = args->arg(0)->asInt()->evalInt(); |
140 |
if (!id) { |
141 |
wrnMsg("ignore_event(): event ID argument may not be zero"); |
142 |
// not errorResult(), because that would abort the script, not intentional in this case |
143 |
return successResult(); |
144 |
} |
145 |
pEngineChannel->IgnoreEventByScriptID(id); |
146 |
} else if (args->arg(0)->exprType() == INT_ARR_EXPR) { |
147 |
VMIntArrayExpr* ids = args->arg(0)->asIntArray(); |
148 |
for (int i = 0; i < ids->arraySize(); ++i) { |
149 |
const ScriptID id = ids->evalIntElement(i); |
150 |
pEngineChannel->IgnoreEventByScriptID(id); |
151 |
} |
152 |
} |
153 |
|
154 |
return successResult(); |
155 |
} |
156 |
|
157 |
// ignore_controller() function |
158 |
|
159 |
InstrumentScriptVMFunction_ignore_controller::InstrumentScriptVMFunction_ignore_controller(InstrumentScriptVM* parent) |
160 |
: m_vm(parent) |
161 |
{ |
162 |
} |
163 |
|
164 |
VMFnResult* InstrumentScriptVMFunction_ignore_controller::exec(VMFnArgs* args) { |
165 |
const ScriptID id = (args->argsCount() >= 1) ? args->arg(0)->asInt()->evalInt() : m_vm->m_event->id; |
166 |
if (!id && args->argsCount() >= 1) { |
167 |
wrnMsg("ignore_controller(): event ID argument may not be zero"); |
168 |
return successResult(); |
169 |
} |
170 |
|
171 |
AbstractEngineChannel* pEngineChannel = |
172 |
static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel); |
173 |
|
174 |
pEngineChannel->IgnoreEventByScriptID(id); |
175 |
|
176 |
return successResult(); |
177 |
} |
178 |
|
179 |
// note_off() function |
180 |
|
181 |
InstrumentScriptVMFunction_note_off::InstrumentScriptVMFunction_note_off(InstrumentScriptVM* parent) |
182 |
: m_vm(parent) |
183 |
{ |
184 |
} |
185 |
|
186 |
bool InstrumentScriptVMFunction_note_off::acceptsArgType(int iArg, ExprType_t type) const { |
187 |
return type == INT_EXPR || type == INT_ARR_EXPR; |
188 |
} |
189 |
|
190 |
VMFnResult* InstrumentScriptVMFunction_note_off::exec(VMFnArgs* args) { |
191 |
AbstractEngineChannel* pEngineChannel = |
192 |
static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel); |
193 |
|
194 |
int velocity = (args->argsCount() >= 2) ? args->arg(1)->asInt()->evalInt() : 127; |
195 |
if (velocity < 0 || velocity > 127) { |
196 |
errMsg("note_off(): argument 2 is an invalid velocity value"); |
197 |
return errorResult(); |
198 |
} |
199 |
|
200 |
if (args->arg(0)->exprType() == INT_EXPR) { |
201 |
const ScriptID id = args->arg(0)->asInt()->evalInt(); |
202 |
if (!id) { |
203 |
wrnMsg("note_off(): note ID for argument 1 may not be zero"); |
204 |
return successResult(); |
205 |
} |
206 |
if (!id.isNoteID()) { |
207 |
wrnMsg("note_off(): argument 1 is not a note ID"); |
208 |
return successResult(); |
209 |
} |
210 |
|
211 |
NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() ); |
212 |
if (!pNote) return successResult(); |
213 |
|
214 |
Event e = pNote->cause; |
215 |
e.Init(); // clear IDs |
216 |
e.CopyTimeFrom(m_vm->m_event->cause); // set fragment time for "now" |
217 |
e.Type = Event::type_note_off; |
218 |
e.Param.Note.Velocity = velocity; |
219 |
|
220 |
pEngineChannel->ScheduleEventMicroSec(&e, 0); |
221 |
} else if (args->arg(0)->exprType() == INT_ARR_EXPR) { |
222 |
VMIntArrayExpr* ids = args->arg(0)->asIntArray(); |
223 |
for (int i = 0; i < ids->arraySize(); ++i) { |
224 |
const ScriptID id = ids->evalIntElement(i); |
225 |
if (!id || !id.isNoteID()) continue; |
226 |
|
227 |
NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() ); |
228 |
if (!pNote) continue; |
229 |
|
230 |
Event e = pNote->cause; |
231 |
e.Init(); // clear IDs |
232 |
e.CopyTimeFrom(m_vm->m_event->cause); // set fragment time for "now" |
233 |
e.Type = Event::type_note_off; |
234 |
e.Param.Note.Velocity = velocity; |
235 |
|
236 |
pEngineChannel->ScheduleEventMicroSec(&e, 0); |
237 |
} |
238 |
} |
239 |
|
240 |
return successResult(); |
241 |
} |
242 |
|
243 |
// set_event_mark() function |
244 |
|
245 |
InstrumentScriptVMFunction_set_event_mark::InstrumentScriptVMFunction_set_event_mark(InstrumentScriptVM* parent) |
246 |
: m_vm(parent) |
247 |
{ |
248 |
} |
249 |
|
250 |
VMFnResult* InstrumentScriptVMFunction_set_event_mark::exec(VMFnArgs* args) { |
251 |
const ScriptID id = args->arg(0)->asInt()->evalInt(); |
252 |
const int groupID = args->arg(1)->asInt()->evalInt(); |
253 |
|
254 |
if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) { |
255 |
errMsg("set_event_mark(): argument 2 is an invalid group id"); |
256 |
return errorResult(); |
257 |
} |
258 |
|
259 |
AbstractEngineChannel* pEngineChannel = |
260 |
static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel); |
261 |
|
262 |
// check if the event/note still exists |
263 |
switch (id.type()) { |
264 |
case ScriptID::EVENT: { |
265 |
RTList<Event>::Iterator itEvent = pEngineChannel->pEngine->EventByID( id.eventID() ); |
266 |
if (!itEvent) return successResult(); |
267 |
break; |
268 |
} |
269 |
case ScriptID::NOTE: { |
270 |
NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() ); |
271 |
if (!pNote) return successResult(); |
272 |
break; |
273 |
} |
274 |
} |
275 |
|
276 |
pEngineChannel->pScript->eventGroups[groupID].insert(id); |
277 |
|
278 |
return successResult(); |
279 |
} |
280 |
|
281 |
// delete_event_mark() function |
282 |
|
283 |
InstrumentScriptVMFunction_delete_event_mark::InstrumentScriptVMFunction_delete_event_mark(InstrumentScriptVM* parent) |
284 |
: m_vm(parent) |
285 |
{ |
286 |
} |
287 |
|
288 |
VMFnResult* InstrumentScriptVMFunction_delete_event_mark::exec(VMFnArgs* args) { |
289 |
const ScriptID id = args->arg(0)->asInt()->evalInt(); |
290 |
const int groupID = args->arg(1)->asInt()->evalInt(); |
291 |
|
292 |
if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) { |
293 |
errMsg("delete_event_mark(): argument 2 is an invalid group id"); |
294 |
return errorResult(); |
295 |
} |
296 |
|
297 |
AbstractEngineChannel* pEngineChannel = |
298 |
static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel); |
299 |
|
300 |
pEngineChannel->pScript->eventGroups[groupID].erase(id); |
301 |
|
302 |
return successResult(); |
303 |
} |
304 |
|
305 |
// by_marks() function |
306 |
|
307 |
InstrumentScriptVMFunction_by_marks::InstrumentScriptVMFunction_by_marks(InstrumentScriptVM* parent) |
308 |
: m_vm(parent) |
309 |
{ |
310 |
} |
311 |
|
312 |
int InstrumentScriptVMFunction_by_marks::Result::arraySize() const { |
313 |
return eventGroup->size(); |
314 |
} |
315 |
|
316 |
int InstrumentScriptVMFunction_by_marks::Result::evalIntElement(uint i) { |
317 |
return (*eventGroup)[i]; |
318 |
} |
319 |
|
320 |
VMFnResult* InstrumentScriptVMFunction_by_marks::errorResult() { |
321 |
m_result.eventGroup = NULL; |
322 |
m_result.flags = StmtFlags_t(STMT_ABORT_SIGNALLED | STMT_ERROR_OCCURRED); |
323 |
return &m_result; |
324 |
} |
325 |
|
326 |
VMFnResult* InstrumentScriptVMFunction_by_marks::successResult(EventGroup* eventGroup) { |
327 |
m_result.eventGroup = eventGroup; |
328 |
m_result.flags = STMT_SUCCESS; |
329 |
return &m_result; |
330 |
} |
331 |
|
332 |
VMFnResult* InstrumentScriptVMFunction_by_marks::exec(VMFnArgs* args) { |
333 |
int groupID = args->arg(0)->asInt()->evalInt(); |
334 |
|
335 |
if (groupID < 0 || groupID >= INSTR_SCRIPT_EVENT_GROUPS) { |
336 |
errMsg("by_marks(): argument is an invalid group id"); |
337 |
return errorResult(); |
338 |
} |
339 |
|
340 |
AbstractEngineChannel* pEngineChannel = |
341 |
static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel); |
342 |
|
343 |
return successResult( &pEngineChannel->pScript->eventGroups[groupID] ); |
344 |
} |
345 |
|
346 |
// change_vol() function |
347 |
|
348 |
InstrumentScriptVMFunction_change_vol::InstrumentScriptVMFunction_change_vol(InstrumentScriptVM* parent) |
349 |
: m_vm(parent) |
350 |
{ |
351 |
} |
352 |
|
353 |
bool InstrumentScriptVMFunction_change_vol::acceptsArgType(int iArg, ExprType_t type) const { |
354 |
if (iArg == 0) |
355 |
return type == INT_EXPR || type == INT_ARR_EXPR; |
356 |
else |
357 |
return INT_EXPR; |
358 |
} |
359 |
|
360 |
VMFnResult* InstrumentScriptVMFunction_change_vol::exec(VMFnArgs* args) { |
361 |
int volume = args->arg(1)->asInt()->evalInt(); // volume change in milli dB |
362 |
bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false; |
363 |
|
364 |
AbstractEngineChannel* pEngineChannel = |
365 |
static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel); |
366 |
|
367 |
if (args->arg(0)->exprType() == INT_EXPR) { |
368 |
const ScriptID id = args->arg(0)->asInt()->evalInt(); |
369 |
if (!id) { |
370 |
wrnMsg("change_vol(): note ID for argument 1 may not be zero"); |
371 |
return successResult(); |
372 |
} |
373 |
if (!id.isNoteID()) { |
374 |
wrnMsg("change_vol(): argument 1 is not a note ID"); |
375 |
return successResult(); |
376 |
} |
377 |
|
378 |
NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() ); |
379 |
if (!pNote) return successResult(); |
380 |
|
381 |
const float fVolumeLin = RTMath::DecibelToLinRatio(float(volume) / 1000.f); |
382 |
// commented out, performed by EngineBase::ProcessNoteSynthParam() for time accuracy behavior |
383 |
/*if (relative) |
384 |
pNote->Override.Volume *= fVolumeLin; |
385 |
else |
386 |
pNote->Override.Volume = fVolumeLin;*/ |
387 |
|
388 |
Event e = m_vm->m_event->cause; // copy to get fragment time for "now" |
389 |
e.Init(); // clear IDs |
390 |
e.Type = Event::type_note_synth_param; |
391 |
e.Param.NoteSynthParam.NoteID = id.noteID(); |
392 |
e.Param.NoteSynthParam.Type = Event::synth_param_volume; |
393 |
e.Param.NoteSynthParam.Delta = fVolumeLin; |
394 |
e.Param.NoteSynthParam.Relative = relative; |
395 |
|
396 |
pEngineChannel->ScheduleEventMicroSec(&e, 0); |
397 |
} else if (args->arg(0)->exprType() == INT_ARR_EXPR) { |
398 |
VMIntArrayExpr* ids = args->arg(0)->asIntArray(); |
399 |
for (int i = 0; i < ids->arraySize(); ++i) { |
400 |
const ScriptID id = ids->evalIntElement(i); |
401 |
if (!id || !id.isNoteID()) continue; |
402 |
|
403 |
NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() ); |
404 |
if (!pNote) continue; |
405 |
|
406 |
const float fVolumeLin = RTMath::DecibelToLinRatio(float(volume) / 1000.f); |
407 |
// commented out, performed by EngineBase::ProcessNoteSynthParam() for time accuracy behavior |
408 |
/*if (relative) |
409 |
pNote->Override.Volume *= fVolumeLin; |
410 |
else |
411 |
pNote->Override.Volume = fVolumeLin;*/ |
412 |
|
413 |
Event e = m_vm->m_event->cause; // copy to get fragment time for "now" |
414 |
e.Init(); // clear IDs |
415 |
e.Type = Event::type_note_synth_param; |
416 |
e.Param.NoteSynthParam.NoteID = id.noteID(); |
417 |
e.Param.NoteSynthParam.Type = Event::synth_param_volume; |
418 |
e.Param.NoteSynthParam.Delta = fVolumeLin; |
419 |
e.Param.NoteSynthParam.Relative = relative; |
420 |
|
421 |
pEngineChannel->ScheduleEventMicroSec(&e, 0); |
422 |
} |
423 |
} |
424 |
|
425 |
return successResult(); |
426 |
} |
427 |
|
428 |
// change_tune() function |
429 |
|
430 |
InstrumentScriptVMFunction_change_tune::InstrumentScriptVMFunction_change_tune(InstrumentScriptVM* parent) |
431 |
: m_vm(parent) |
432 |
{ |
433 |
} |
434 |
|
435 |
bool InstrumentScriptVMFunction_change_tune::acceptsArgType(int iArg, ExprType_t type) const { |
436 |
if (iArg == 0) |
437 |
return type == INT_EXPR || type == INT_ARR_EXPR; |
438 |
else |
439 |
return INT_EXPR; |
440 |
} |
441 |
|
442 |
VMFnResult* InstrumentScriptVMFunction_change_tune::exec(VMFnArgs* args) { |
443 |
int tune = args->arg(1)->asInt()->evalInt(); // tuning change in milli cents |
444 |
bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false; |
445 |
|
446 |
AbstractEngineChannel* pEngineChannel = |
447 |
static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel); |
448 |
|
449 |
if (args->arg(0)->exprType() == INT_EXPR) { |
450 |
const ScriptID id = args->arg(0)->asInt()->evalInt(); |
451 |
if (!id) { |
452 |
wrnMsg("change_tune(): note ID for argument 1 may not be zero"); |
453 |
return successResult(); |
454 |
} |
455 |
if (!id.isNoteID()) { |
456 |
wrnMsg("change_tune(): argument 1 is not a note ID"); |
457 |
return successResult(); |
458 |
} |
459 |
|
460 |
NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() ); |
461 |
if (!pNote) return successResult(); |
462 |
|
463 |
const float fFreqRatio = RTMath::CentsToFreqRatioUnlimited(float(tune) / 1000.f); |
464 |
// commented out, performed by EngineBase::ProcessNoteSynthParam() for time accuracy behavior |
465 |
/*if (relative) |
466 |
pNote->Override.Pitch *= fFreqRatio; |
467 |
else |
468 |
pNote->Override.Pitch = fFreqRatio;*/ |
469 |
|
470 |
Event e = m_vm->m_event->cause; // copy to get fragment time for "now" |
471 |
e.Init(); // clear IDs |
472 |
e.Type = Event::type_note_synth_param; |
473 |
e.Param.NoteSynthParam.NoteID = id.noteID(); |
474 |
e.Param.NoteSynthParam.Type = Event::synth_param_pitch; |
475 |
e.Param.NoteSynthParam.Delta = fFreqRatio; |
476 |
e.Param.NoteSynthParam.Relative = relative; |
477 |
|
478 |
pEngineChannel->ScheduleEventMicroSec(&e, 0); |
479 |
} else if (args->arg(0)->exprType() == INT_ARR_EXPR) { |
480 |
VMIntArrayExpr* ids = args->arg(0)->asIntArray(); |
481 |
for (int i = 0; i < ids->arraySize(); ++i) { |
482 |
const ScriptID id = ids->evalIntElement(i); |
483 |
if (!id || !id.isNoteID()) continue; |
484 |
|
485 |
NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() ); |
486 |
if (!pNote) continue; |
487 |
|
488 |
const float fFreqRatio = RTMath::CentsToFreqRatioUnlimited(float(tune) / 1000.f); |
489 |
// commented out, performed by EngineBase::ProcessNoteSynthParam() for time accuracy behavior |
490 |
/*if (relative) |
491 |
pNote->Override.Pitch *= fFreqRatio; |
492 |
else |
493 |
pNote->Override.Pitch = fFreqRatio;*/ |
494 |
|
495 |
Event e = m_vm->m_event->cause; // copy to get fragment time for "now" |
496 |
e.Init(); // clear IDs |
497 |
e.Type = Event::type_note_synth_param; |
498 |
e.Param.NoteSynthParam.NoteID = id.noteID(); |
499 |
e.Param.NoteSynthParam.Type = Event::synth_param_pitch; |
500 |
e.Param.NoteSynthParam.Delta = fFreqRatio; |
501 |
e.Param.NoteSynthParam.Relative = relative; |
502 |
|
503 |
pEngineChannel->ScheduleEventMicroSec(&e, 0); |
504 |
} |
505 |
} |
506 |
|
507 |
return successResult(); |
508 |
} |
509 |
|
510 |
// change_pan() function |
511 |
|
512 |
InstrumentScriptVMFunction_change_pan::InstrumentScriptVMFunction_change_pan(InstrumentScriptVM* parent) |
513 |
: m_vm(parent) |
514 |
{ |
515 |
} |
516 |
|
517 |
bool InstrumentScriptVMFunction_change_pan::acceptsArgType(int iArg, ExprType_t type) const { |
518 |
if (iArg == 0) |
519 |
return type == INT_EXPR || type == INT_ARR_EXPR; |
520 |
else |
521 |
return INT_EXPR; |
522 |
} |
523 |
|
524 |
VMFnResult* InstrumentScriptVMFunction_change_pan::exec(VMFnArgs* args) { |
525 |
int pan = args->arg(1)->asInt()->evalInt(); |
526 |
bool relative = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : false; |
527 |
|
528 |
if (pan > 1000) { |
529 |
wrnMsg("change_pan(): argument 2 may not be larger than 1000"); |
530 |
pan = 1000; |
531 |
} else if (pan < -1000) { |
532 |
wrnMsg("change_pan(): argument 2 may not be smaller than -1000"); |
533 |
pan = -1000; |
534 |
} |
535 |
|
536 |
AbstractEngineChannel* pEngineChannel = |
537 |
static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel); |
538 |
|
539 |
if (args->arg(0)->exprType() == INT_EXPR) { |
540 |
const ScriptID id = args->arg(0)->asInt()->evalInt(); |
541 |
if (!id) { |
542 |
wrnMsg("change_pan(): note ID for argument 1 may not be zero"); |
543 |
return successResult(); |
544 |
} |
545 |
if (!id.isNoteID()) { |
546 |
wrnMsg("change_pan(): argument 1 is not a note ID"); |
547 |
return successResult(); |
548 |
} |
549 |
|
550 |
NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() ); |
551 |
if (!pNote) return successResult(); |
552 |
|
553 |
const float fPan = float(pan) / 1000.f; |
554 |
// commented out, performed by EngineBase::ProcessNoteSynthParam() for time accuracy behavior |
555 |
/*if (relative) { |
556 |
pNote->Override.Pan = RTMath::RelativeSummedAvg(pNote->Override.Pan, fPan, ++pNote->Override.PanSources); |
557 |
} else { |
558 |
pNote->Override.Pan = fPan; |
559 |
pNote->Override.PanSources = 1; // only relevant on subsequent change_pan() calls on same note with 'relative' being set |
560 |
}*/ |
561 |
|
562 |
Event e = m_vm->m_event->cause; // copy to get fragment time for "now" |
563 |
e.Init(); // clear IDs |
564 |
e.Type = Event::type_note_synth_param; |
565 |
e.Param.NoteSynthParam.NoteID = id.noteID(); |
566 |
e.Param.NoteSynthParam.Type = Event::synth_param_pan; |
567 |
e.Param.NoteSynthParam.Delta = fPan; |
568 |
e.Param.NoteSynthParam.Relative = relative; |
569 |
|
570 |
pEngineChannel->ScheduleEventMicroSec(&e, 0); |
571 |
} else if (args->arg(0)->exprType() == INT_ARR_EXPR) { |
572 |
VMIntArrayExpr* ids = args->arg(0)->asIntArray(); |
573 |
for (int i = 0; i < ids->arraySize(); ++i) { |
574 |
const ScriptID id = ids->evalIntElement(i); |
575 |
if (!id || !id.isNoteID()) continue; |
576 |
|
577 |
NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() ); |
578 |
if (!pNote) continue; |
579 |
|
580 |
const float fPan = float(pan) / 1000.f; |
581 |
// commented out, performed by EngineBase::ProcessNoteSynthParam() for time accuracy behavior |
582 |
/*if (relative) { |
583 |
pNote->Override.Pan = RTMath::RelativeSummedAvg(pNote->Override.Pan, fPan, ++pNote->Override.PanSources); |
584 |
} else { |
585 |
pNote->Override.Pan = fPan; |
586 |
pNote->Override.PanSources = 1; // only relevant on subsequent change_pan() calls on same note with 'relative' being set |
587 |
}*/ |
588 |
|
589 |
Event e = m_vm->m_event->cause; // copy to get fragment time for "now" |
590 |
e.Init(); // clear IDs |
591 |
e.Type = Event::type_note_synth_param; |
592 |
e.Param.NoteSynthParam.NoteID = id.noteID(); |
593 |
e.Param.NoteSynthParam.Type = Event::synth_param_pan; |
594 |
e.Param.NoteSynthParam.Delta = fPan; |
595 |
e.Param.NoteSynthParam.Relative = relative; |
596 |
|
597 |
pEngineChannel->ScheduleEventMicroSec(&e, 0); |
598 |
} |
599 |
} |
600 |
|
601 |
return successResult(); |
602 |
} |
603 |
|
604 |
#define VM_FILTER_PAR_MAX_VALUE 1000000 |
605 |
|
606 |
// change_cutoff() function |
607 |
|
608 |
InstrumentScriptVMFunction_change_cutoff::InstrumentScriptVMFunction_change_cutoff(InstrumentScriptVM* parent) |
609 |
: m_vm(parent) |
610 |
{ |
611 |
} |
612 |
|
613 |
bool InstrumentScriptVMFunction_change_cutoff::acceptsArgType(int iArg, ExprType_t type) const { |
614 |
if (iArg == 0) |
615 |
return type == INT_EXPR || type == INT_ARR_EXPR; |
616 |
else |
617 |
return INT_EXPR; |
618 |
} |
619 |
|
620 |
VMFnResult* InstrumentScriptVMFunction_change_cutoff::exec(VMFnArgs* args) { |
621 |
int cutoff = args->arg(1)->asInt()->evalInt(); |
622 |
if (cutoff > VM_FILTER_PAR_MAX_VALUE) { |
623 |
wrnMsg("change_cutoff(): argument 2 may not be larger than 1000000"); |
624 |
cutoff = VM_FILTER_PAR_MAX_VALUE; |
625 |
} else if (cutoff < 0) { |
626 |
wrnMsg("change_cutoff(): argument 2 may not be negative"); |
627 |
cutoff = 0; |
628 |
} |
629 |
|
630 |
AbstractEngineChannel* pEngineChannel = |
631 |
static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel); |
632 |
|
633 |
if (args->arg(0)->exprType() == INT_EXPR) { |
634 |
const ScriptID id = args->arg(0)->asInt()->evalInt(); |
635 |
if (!id) { |
636 |
wrnMsg("change_cutoff(): note ID for argument 1 may not be zero"); |
637 |
return successResult(); |
638 |
} |
639 |
if (!id.isNoteID()) { |
640 |
wrnMsg("change_cutoff(): argument 1 is not a note ID"); |
641 |
return successResult(); |
642 |
} |
643 |
|
644 |
NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() ); |
645 |
if (!pNote) return successResult(); |
646 |
|
647 |
const float fCutoff = float(cutoff) / float(VM_FILTER_PAR_MAX_VALUE); |
648 |
|
649 |
Event e = m_vm->m_event->cause; // copy to get fragment time for "now" |
650 |
e.Init(); // clear IDs |
651 |
e.Type = Event::type_note_synth_param; |
652 |
e.Param.NoteSynthParam.NoteID = id.noteID(); |
653 |
e.Param.NoteSynthParam.Type = Event::synth_param_cutoff; |
654 |
e.Param.NoteSynthParam.Delta = fCutoff; |
655 |
e.Param.NoteSynthParam.Relative = false; |
656 |
|
657 |
pEngineChannel->ScheduleEventMicroSec(&e, 0); |
658 |
} else if (args->arg(0)->exprType() == INT_ARR_EXPR) { |
659 |
VMIntArrayExpr* ids = args->arg(0)->asIntArray(); |
660 |
for (int i = 0; i < ids->arraySize(); ++i) { |
661 |
const ScriptID id = ids->evalIntElement(i); |
662 |
if (!id || !id.isNoteID()) continue; |
663 |
|
664 |
NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() ); |
665 |
if (!pNote) continue; |
666 |
|
667 |
const float fCutoff = float(cutoff) / float(VM_FILTER_PAR_MAX_VALUE); |
668 |
|
669 |
Event e = m_vm->m_event->cause; // copy to get fragment time for "now" |
670 |
e.Init(); // clear IDs |
671 |
e.Type = Event::type_note_synth_param; |
672 |
e.Param.NoteSynthParam.NoteID = id.noteID(); |
673 |
e.Param.NoteSynthParam.Type = Event::synth_param_cutoff; |
674 |
e.Param.NoteSynthParam.Delta = fCutoff; |
675 |
e.Param.NoteSynthParam.Relative = false; |
676 |
|
677 |
pEngineChannel->ScheduleEventMicroSec(&e, 0); |
678 |
} |
679 |
} |
680 |
|
681 |
return successResult(); |
682 |
} |
683 |
|
684 |
// change_reso() function |
685 |
|
686 |
InstrumentScriptVMFunction_change_reso::InstrumentScriptVMFunction_change_reso(InstrumentScriptVM* parent) |
687 |
: m_vm(parent) |
688 |
{ |
689 |
} |
690 |
|
691 |
bool InstrumentScriptVMFunction_change_reso::acceptsArgType(int iArg, ExprType_t type) const { |
692 |
if (iArg == 0) |
693 |
return type == INT_EXPR || type == INT_ARR_EXPR; |
694 |
else |
695 |
return INT_EXPR; |
696 |
} |
697 |
|
698 |
VMFnResult* InstrumentScriptVMFunction_change_reso::exec(VMFnArgs* args) { |
699 |
int resonance = args->arg(1)->asInt()->evalInt(); |
700 |
if (resonance > VM_FILTER_PAR_MAX_VALUE) { |
701 |
wrnMsg("change_reso(): argument 2 may not be larger than 1000000"); |
702 |
resonance = VM_FILTER_PAR_MAX_VALUE; |
703 |
} else if (resonance < 0) { |
704 |
wrnMsg("change_reso(): argument 2 may not be negative"); |
705 |
resonance = 0; |
706 |
} |
707 |
|
708 |
AbstractEngineChannel* pEngineChannel = |
709 |
static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel); |
710 |
|
711 |
if (args->arg(0)->exprType() == INT_EXPR) { |
712 |
const ScriptID id = args->arg(0)->asInt()->evalInt(); |
713 |
if (!id) { |
714 |
wrnMsg("change_reso(): note ID for argument 1 may not be zero"); |
715 |
return successResult(); |
716 |
} |
717 |
if (!id.isNoteID()) { |
718 |
wrnMsg("change_reso(): argument 1 is not a note ID"); |
719 |
return successResult(); |
720 |
} |
721 |
|
722 |
NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() ); |
723 |
if (!pNote) return successResult(); |
724 |
|
725 |
const float fResonance = float(resonance) / float(VM_FILTER_PAR_MAX_VALUE); |
726 |
|
727 |
Event e = m_vm->m_event->cause; // copy to get fragment time for "now" |
728 |
e.Init(); // clear IDs |
729 |
e.Type = Event::type_note_synth_param; |
730 |
e.Param.NoteSynthParam.NoteID = id.noteID(); |
731 |
e.Param.NoteSynthParam.Type = Event::synth_param_resonance; |
732 |
e.Param.NoteSynthParam.Delta = fResonance; |
733 |
e.Param.NoteSynthParam.Relative = false; |
734 |
|
735 |
pEngineChannel->ScheduleEventMicroSec(&e, 0); |
736 |
} else if (args->arg(0)->exprType() == INT_ARR_EXPR) { |
737 |
VMIntArrayExpr* ids = args->arg(0)->asIntArray(); |
738 |
for (int i = 0; i < ids->arraySize(); ++i) { |
739 |
const ScriptID id = ids->evalIntElement(i); |
740 |
if (!id || !id.isNoteID()) continue; |
741 |
|
742 |
NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() ); |
743 |
if (!pNote) continue; |
744 |
|
745 |
const float fResonance = float(resonance) / float(VM_FILTER_PAR_MAX_VALUE); |
746 |
|
747 |
Event e = m_vm->m_event->cause; // copy to get fragment time for "now" |
748 |
e.Init(); // clear IDs |
749 |
e.Type = Event::type_note_synth_param; |
750 |
e.Param.NoteSynthParam.NoteID = id.noteID(); |
751 |
e.Param.NoteSynthParam.Type = Event::synth_param_resonance; |
752 |
e.Param.NoteSynthParam.Delta = fResonance; |
753 |
e.Param.NoteSynthParam.Relative = false; |
754 |
|
755 |
pEngineChannel->ScheduleEventMicroSec(&e, 0); |
756 |
} |
757 |
} |
758 |
|
759 |
return successResult(); |
760 |
} |
761 |
|
762 |
// event_status() function |
763 |
|
764 |
InstrumentScriptVMFunction_event_status::InstrumentScriptVMFunction_event_status(InstrumentScriptVM* parent) |
765 |
: m_vm(parent) |
766 |
{ |
767 |
} |
768 |
|
769 |
VMFnResult* InstrumentScriptVMFunction_event_status::exec(VMFnArgs* args) { |
770 |
AbstractEngineChannel* pEngineChannel = |
771 |
static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel); |
772 |
|
773 |
const ScriptID id = args->arg(0)->asInt()->evalInt(); |
774 |
if (!id) { |
775 |
wrnMsg("event_status(): note ID for argument 1 may not be zero"); |
776 |
return successResult(EVENT_STATUS_INACTIVE); |
777 |
} |
778 |
if (!id.isNoteID()) { |
779 |
wrnMsg("event_status(): argument 1 is not a note ID"); |
780 |
return successResult(EVENT_STATUS_INACTIVE); |
781 |
} |
782 |
|
783 |
NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() ); |
784 |
return successResult(pNote ? EVENT_STATUS_NOTE_QUEUE : EVENT_STATUS_INACTIVE); |
785 |
} |
786 |
|
787 |
} // namespace LinuxSampler |