46 |
ResetControllers(); |
ResetControllers(); |
47 |
PortamentoMode = false; |
PortamentoMode = false; |
48 |
PortamentoTime = CONFIG_PORTAMENTO_TIME_DEFAULT; |
PortamentoTime = CONFIG_PORTAMENTO_TIME_DEFAULT; |
49 |
pScriptEvents = NULL; |
pScript = NULL; |
50 |
} |
} |
51 |
|
|
52 |
AbstractEngineChannel::~AbstractEngineChannel() { |
AbstractEngineChannel::~AbstractEngineChannel() { |
|
unloadCurrentInstrumentScript(); |
|
|
if (pScriptEvents) delete pScriptEvents; |
|
53 |
delete pEventQueue; |
delete pEventQueue; |
54 |
DeleteGroupEventLists(); |
DeleteGroupEventLists(); |
55 |
RemoveAllFxSends(); |
RemoveAllFxSends(); |
143 |
} |
} |
144 |
|
|
145 |
/** |
/** |
|
* Loads the real-time instrument script given by @a text on this engine |
|
|
* channel. A resource manager is used to allocate and share equivalent |
|
|
* scripts on multiple engine channels. |
|
|
* |
|
|
* @param text - source code of script |
|
|
*/ |
|
|
void AbstractEngineChannel::loadInstrumentScript(const String& text) { |
|
|
dmsg(1,("Loading real-time instrument script ... ")); |
|
|
|
|
|
// hand back old script reference and VM execution contexts |
|
|
// (if not done already) |
|
|
unloadCurrentInstrumentScript(); |
|
|
|
|
|
// get new script reference |
|
|
script.parserContext = pEngine->scripts.Borrow(text, this); |
|
|
if (!script.parserContext->errors().empty()) { |
|
|
std::vector<ParserIssue> errors = script.parserContext->errors(); |
|
|
std::cerr << "[ScriptVM] Could not load instrument script, there were " |
|
|
<< errors.size() << " parser errors:\n"; |
|
|
for (int i = 0; i < errors.size(); ++i) |
|
|
errors[i].dump(); |
|
|
return; // stop here if there were any parser errors |
|
|
} |
|
|
|
|
|
script.handlerInit = script.parserContext->eventHandlerByName("init"); |
|
|
script.handlerNote = script.parserContext->eventHandlerByName("note"); |
|
|
script.handlerController = script.parserContext->eventHandlerByName("controller"); |
|
|
script.bHasValidScript = |
|
|
script.handlerInit || script.handlerNote || script.handlerController; |
|
|
|
|
|
// amount of script handlers each script event has to execute |
|
|
int handlerExecCount = 0; |
|
|
if (script.handlerInit) handlerExecCount++; |
|
|
if (script.handlerNote || script.handlerController) handlerExecCount++; |
|
|
|
|
|
// create script event pool (if it doesn't exist already) |
|
|
if (!pScriptEvents) |
|
|
pScriptEvents = new Pool<ScriptEvent>(CONFIG_MAX_EVENTS_PER_FRAGMENT); |
|
|
|
|
|
// create new VM execution contexts for new script |
|
|
while (!pScriptEvents->poolIsEmpty()) { |
|
|
RTList<ScriptEvent>::Iterator it = pScriptEvents->allocAppend(); |
|
|
it->execCtx = pEngine->pScriptVM->createExecContext( |
|
|
script.parserContext |
|
|
); |
|
|
it->handlers = new VMEventHandler*[handlerExecCount+1]; |
|
|
} |
|
|
pScriptEvents->clear(); |
|
|
|
|
|
dmsg(1,("Done\n")); |
|
|
} |
|
|
|
|
|
/** |
|
|
* Unloads the currently used real-time instrument script on this sampler |
|
|
* channel. A resource manager is used to share equivalent scripts among |
|
|
* multiple sampler channels, and to deallocate the parsed script once not |
|
|
* used on any engine channel anymore. |
|
|
*/ |
|
|
void AbstractEngineChannel::unloadCurrentInstrumentScript() { |
|
|
if (script.parserContext) |
|
|
dmsg(1,("Unloading current instrument script.")); |
|
|
|
|
|
// free allocated VM execution contexts |
|
|
if (pScriptEvents) { |
|
|
pScriptEvents->clear(); |
|
|
while (!pScriptEvents->poolIsEmpty()) { |
|
|
RTList<ScriptEvent>::Iterator it = pScriptEvents->allocAppend(); |
|
|
if (it->execCtx) { |
|
|
// free VM execution context object |
|
|
delete it->execCtx; |
|
|
it->execCtx = NULL; |
|
|
// free C array of handler pointers |
|
|
delete [] it->handlers; |
|
|
} |
|
|
} |
|
|
pScriptEvents->clear(); |
|
|
} |
|
|
// hand back VM representation of script |
|
|
if (script.parserContext) { |
|
|
pEngine->scripts.HandBack(script.parserContext, this); |
|
|
script.parserContext = NULL; |
|
|
script.handlerInit = NULL; |
|
|
script.handlerNote = NULL; |
|
|
script.handlerController = NULL; |
|
|
} |
|
|
script.bHasValidScript = false; |
|
|
} |
|
|
|
|
|
/** |
|
146 |
* Implementation of virtual method from abstract EngineChannel interface. |
* Implementation of virtual method from abstract EngineChannel interface. |
147 |
* This method will periodically be polled (e.g. by the LSCP server) to |
* This method will periodically be polled (e.g. by the LSCP server) to |
148 |
* check if some engine channel parameter has changed since the last |
* check if some engine channel parameter has changed since the last |
376 |
event.Param.Note.Key = Key; |
event.Param.Note.Key = Key; |
377 |
event.Param.Note.Velocity = Velocity; |
event.Param.Note.Velocity = Velocity; |
378 |
event.Param.Note.Channel = MidiChannel; |
event.Param.Note.Channel = MidiChannel; |
379 |
|
memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes |
380 |
event.pEngineChannel = this; |
event.pEngineChannel = this; |
381 |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
382 |
else dmsg(1,("EngineChannel: Input event queue full!")); |
else dmsg(1,("EngineChannel: Input event queue full!")); |
418 |
event.Param.Note.Key = Key; |
event.Param.Note.Key = Key; |
419 |
event.Param.Note.Velocity = Velocity; |
event.Param.Note.Velocity = Velocity; |
420 |
event.Param.Note.Channel = MidiChannel; |
event.Param.Note.Channel = MidiChannel; |
421 |
|
memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes |
422 |
event.pEngineChannel = this; |
event.pEngineChannel = this; |
423 |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
424 |
else dmsg(1,("EngineChannel: Input event queue full!")); |
else dmsg(1,("EngineChannel: Input event queue full!")); |
455 |
event.Param.Note.Key = Key; |
event.Param.Note.Key = Key; |
456 |
event.Param.Note.Velocity = Velocity; |
event.Param.Note.Velocity = Velocity; |
457 |
event.Param.Note.Channel = MidiChannel; |
event.Param.Note.Channel = MidiChannel; |
458 |
|
memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes |
459 |
event.pEngineChannel = this; |
event.pEngineChannel = this; |
460 |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
461 |
else dmsg(1,("EngineChannel: Input event queue full!")); |
else dmsg(1,("EngineChannel: Input event queue full!")); |
497 |
event.Param.Note.Key = Key; |
event.Param.Note.Key = Key; |
498 |
event.Param.Note.Velocity = Velocity; |
event.Param.Note.Velocity = Velocity; |
499 |
event.Param.Note.Channel = MidiChannel; |
event.Param.Note.Channel = MidiChannel; |
500 |
|
memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes |
501 |
event.pEngineChannel = this; |
event.pEngineChannel = this; |
502 |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
503 |
else dmsg(1,("EngineChannel: Input event queue full!")); |
else dmsg(1,("EngineChannel: Input event queue full!")); |
532 |
event.Type = Event::type_pitchbend; |
event.Type = Event::type_pitchbend; |
533 |
event.Param.Pitch.Pitch = Pitch; |
event.Param.Pitch.Pitch = Pitch; |
534 |
event.Param.Pitch.Channel = MidiChannel; |
event.Param.Pitch.Channel = MidiChannel; |
535 |
|
memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes |
536 |
event.pEngineChannel = this; |
event.pEngineChannel = this; |
537 |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
538 |
else dmsg(1,("EngineChannel: Input event queue full!")); |
else dmsg(1,("EngineChannel: Input event queue full!")); |
562 |
event.Type = Event::type_pitchbend; |
event.Type = Event::type_pitchbend; |
563 |
event.Param.Pitch.Pitch = Pitch; |
event.Param.Pitch.Pitch = Pitch; |
564 |
event.Param.Pitch.Channel = MidiChannel; |
event.Param.Pitch.Channel = MidiChannel; |
565 |
|
memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes |
566 |
event.pEngineChannel = this; |
event.pEngineChannel = this; |
567 |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
568 |
else dmsg(1,("AbstractEngineChannel: Input event queue full!")); |
else dmsg(1,("AbstractEngineChannel: Input event queue full!")); |
589 |
event.Param.CC.Controller = Controller; |
event.Param.CC.Controller = Controller; |
590 |
event.Param.CC.Value = Value; |
event.Param.CC.Value = Value; |
591 |
event.Param.CC.Channel = MidiChannel; |
event.Param.CC.Channel = MidiChannel; |
592 |
|
memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes |
593 |
event.pEngineChannel = this; |
event.pEngineChannel = this; |
594 |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
595 |
else dmsg(1,("AbstractEngineChannel: Input event queue full!")); |
else dmsg(1,("AbstractEngineChannel: Input event queue full!")); |
621 |
event.Param.CC.Controller = Controller; |
event.Param.CC.Controller = Controller; |
622 |
event.Param.CC.Value = Value; |
event.Param.CC.Value = Value; |
623 |
event.Param.CC.Channel = MidiChannel; |
event.Param.CC.Channel = MidiChannel; |
624 |
|
memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes |
625 |
event.pEngineChannel = this; |
event.pEngineChannel = this; |
626 |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
627 |
else dmsg(1,("AbstractEngineChannel: Input event queue full!")); |
else dmsg(1,("AbstractEngineChannel: Input event queue full!")); |
636 |
|
|
637 |
Event event = pEngine->pEventGenerator->CreateEvent(); |
Event event = pEngine->pEventGenerator->CreateEvent(); |
638 |
event.Type = Event::type_channel_pressure; |
event.Type = Event::type_channel_pressure; |
639 |
|
event.Param.ChannelPressure.Controller = CTRL_TABLE_IDX_AFTERTOUCH; // required for instrument scripts |
640 |
event.Param.ChannelPressure.Value = Value; |
event.Param.ChannelPressure.Value = Value; |
641 |
event.Param.ChannelPressure.Channel = MidiChannel; |
event.Param.ChannelPressure.Channel = MidiChannel; |
642 |
|
memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes |
643 |
event.pEngineChannel = this; |
event.pEngineChannel = this; |
644 |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
645 |
else dmsg(1,("AbstractEngineChannel: Input event queue full!")); |
else dmsg(1,("AbstractEngineChannel: Input event queue full!")); |
654 |
|
|
655 |
Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos); |
Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos); |
656 |
event.Type = Event::type_channel_pressure; |
event.Type = Event::type_channel_pressure; |
657 |
|
event.Param.ChannelPressure.Controller = CTRL_TABLE_IDX_AFTERTOUCH; // required for instrument scripts |
658 |
event.Param.ChannelPressure.Value = Value; |
event.Param.ChannelPressure.Value = Value; |
659 |
event.Param.ChannelPressure.Channel = MidiChannel; |
event.Param.ChannelPressure.Channel = MidiChannel; |
660 |
|
memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes |
661 |
event.pEngineChannel = this; |
event.pEngineChannel = this; |
662 |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
663 |
else dmsg(1,("AbstractEngineChannel: Input event queue full!")); |
else dmsg(1,("AbstractEngineChannel: Input event queue full!")); |
675 |
event.Param.NotePressure.Key = Key; |
event.Param.NotePressure.Key = Key; |
676 |
event.Param.NotePressure.Value = Value; |
event.Param.NotePressure.Value = Value; |
677 |
event.Param.NotePressure.Channel = MidiChannel; |
event.Param.NotePressure.Channel = MidiChannel; |
678 |
|
memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes |
679 |
event.pEngineChannel = this; |
event.pEngineChannel = this; |
680 |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
681 |
else dmsg(1,("AbstractEngineChannel: Input event queue full!")); |
else dmsg(1,("AbstractEngineChannel: Input event queue full!")); |
693 |
event.Param.NotePressure.Key = Key; |
event.Param.NotePressure.Key = Key; |
694 |
event.Param.NotePressure.Value = Value; |
event.Param.NotePressure.Value = Value; |
695 |
event.Param.NotePressure.Channel = MidiChannel; |
event.Param.NotePressure.Channel = MidiChannel; |
696 |
|
memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes |
697 |
event.pEngineChannel = this; |
event.pEngineChannel = this; |
698 |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
699 |
else dmsg(1,("AbstractEngineChannel: Input event queue full!")); |
else dmsg(1,("AbstractEngineChannel: Input event queue full!")); |
771 |
<< devEvent.Type << "). This is a bug!"; |
<< devEvent.Type << "). This is a bug!"; |
772 |
continue; |
continue; |
773 |
} |
} |
774 |
|
memset(&event.Format, 0, sizeof(event.Format)); // init format specific stuff with zeroes |
775 |
event.pEngineChannel = this; |
event.pEngineChannel = this; |
776 |
// copy event to internal event list |
// copy event to internal event list |
777 |
if (pEvents->poolIsEmpty()) { |
if (pEvents->poolIsEmpty()) { |
808 |
eventQueueReader.free(); // free all copied events from input queue |
eventQueueReader.free(); // free all copied events from input queue |
809 |
} |
} |
810 |
|
|
811 |
|
/** |
812 |
|
* Called by real-time instrument script functions to schedule a new event |
813 |
|
* somewhere in future. |
814 |
|
* |
815 |
|
* @returns unique event ID of scheduled new event |
816 |
|
*/ |
817 |
|
int AbstractEngineChannel::ScheduleEvent(const Event* pEvent, int delay) { //TODO: delay not implemented yet |
818 |
|
// since delay is not implemented yet, we simply add the new event |
819 |
|
// to the event list of the current audio fragmet cycle for now |
820 |
|
RTList<Event>::Iterator itEvent = pEvents->allocAppend(); |
821 |
|
if (itEvent) *itEvent = *pEvent; // copy event |
822 |
|
return pEvents->getID(itEvent); |
823 |
|
} |
824 |
|
|
825 |
|
/** |
826 |
|
* Called by real-time instrument script functions to ignore the event |
827 |
|
* reflected by given event ID. The event will be freed immediately to its |
828 |
|
* pool and cannot be dereferenced by its old ID anymore. Even if its |
829 |
|
* allocated back from the Pool later on, it will have a different ID. |
830 |
|
*/ |
831 |
|
void AbstractEngineChannel::IgnoreEvent(int id) { |
832 |
|
RTList<Event>::Iterator it = pEvents->fromID(id); |
833 |
|
if (it) pEvents->free(it); |
834 |
|
} |
835 |
|
|
836 |
FxSend* AbstractEngineChannel::AddFxSend(uint8_t MidiCtrl, String Name) throw (Exception) { |
FxSend* AbstractEngineChannel::AddFxSend(uint8_t MidiCtrl, String Name) throw (Exception) { |
837 |
if (pEngine) pEngine->DisableAndLock(); |
if (pEngine) pEngine->DisableAndLock(); |
838 |
FxSend* pFxSend = new FxSend(this, MidiCtrl, Name); |
FxSend* pFxSend = new FxSend(this, MidiCtrl, Name); |