5 |
* Copyright (C) 2003,2004 by Benno Senoner and Christian Schoenebeck * |
* Copyright (C) 2003,2004 by Benno Senoner and Christian Schoenebeck * |
6 |
* Copyright (C) 2005-2008 Christian Schoenebeck * |
* Copyright (C) 2005-2008 Christian Schoenebeck * |
7 |
* Copyright (C) 2009-2012 Christian Schoenebeck and Grigor Iliev * |
* Copyright (C) 2009-2012 Christian Schoenebeck and Grigor Iliev * |
8 |
* Copyright (C) 2013-2014 Christian Schoenebeck and Andreas Persson * |
* Copyright (C) 2012-2016 Christian Schoenebeck and Andreas Persson * |
9 |
* * |
* * |
10 |
* This program is free software; you can redistribute it and/or modify * |
* This program is free software; you can redistribute it and/or modify * |
11 |
* it under the terms of the GNU General Public License as published by * |
* it under the terms of the GNU General Public License as published by * |
35 |
{ |
{ |
36 |
pEngine = NULL; |
pEngine = NULL; |
37 |
pEvents = NULL; // we allocate when we retrieve the right Engine object |
pEvents = NULL; // we allocate when we retrieve the right Engine object |
38 |
|
delayedEvents.pList = NULL; |
39 |
pEventQueue = new RingBuffer<Event,false>(CONFIG_MAX_EVENTS_PER_FRAGMENT, 0); |
pEventQueue = new RingBuffer<Event,false>(CONFIG_MAX_EVENTS_PER_FRAGMENT, 0); |
40 |
InstrumentIdx = -1; |
InstrumentIdx = -1; |
41 |
InstrumentStat = -1; |
InstrumentStat = -1; |
47 |
ResetControllers(); |
ResetControllers(); |
48 |
PortamentoMode = false; |
PortamentoMode = false; |
49 |
PortamentoTime = CONFIG_PORTAMENTO_TIME_DEFAULT; |
PortamentoTime = CONFIG_PORTAMENTO_TIME_DEFAULT; |
50 |
pScriptEvents = NULL; |
pScript = NULL; |
51 |
} |
} |
52 |
|
|
53 |
AbstractEngineChannel::~AbstractEngineChannel() { |
AbstractEngineChannel::~AbstractEngineChannel() { |
|
unloadCurrentInstrumentScript(); |
|
|
if (pScriptEvents) delete pScriptEvents; |
|
54 |
delete pEventQueue; |
delete pEventQueue; |
55 |
DeleteGroupEventLists(); |
DeleteGroupEventLists(); |
56 |
RemoveAllFxSends(); |
RemoveAllFxSends(); |
102 |
|
|
103 |
void AbstractEngineChannel::Reset() { |
void AbstractEngineChannel::Reset() { |
104 |
if (pEngine) pEngine->DisableAndLock(); |
if (pEngine) pEngine->DisableAndLock(); |
105 |
ResetInternal(); |
ResetInternal(false/*don't reset engine*/); |
106 |
ResetControllers(); |
ResetControllers(); |
107 |
if (pEngine) { |
if (pEngine) { |
108 |
pEngine->Enable(); |
pEngine->Enable(); |
130 |
/** |
/** |
131 |
* This method is not thread safe! |
* This method is not thread safe! |
132 |
*/ |
*/ |
133 |
void AbstractEngineChannel::ResetInternal() { |
void AbstractEngineChannel::ResetInternal(bool bResetEngine) { |
134 |
CurrentKeyDimension = 0; |
CurrentKeyDimension = 0; |
135 |
PortamentoPos = -1.0f; // no portamento active yet |
PortamentoPos = -1.0f; // no portamento active yet |
136 |
|
|
137 |
|
// delete all active instrument script events |
138 |
|
if (pScript) pScript->resetEvents(); |
139 |
|
|
140 |
|
// free all delayed MIDI events |
141 |
|
delayedEvents.clear(); |
142 |
|
|
143 |
// delete all input events |
// delete all input events |
144 |
pEventQueue->init(); |
pEventQueue->init(); |
145 |
|
|
146 |
if (pEngine) pEngine->ResetInternal(); |
if (bResetEngine && pEngine) pEngine->ResetInternal(); |
147 |
|
|
148 |
// status of engine channel has changed, so set notify flag |
// status of engine channel has changed, so set notify flag |
149 |
bStatusChanged = true; |
bStatusChanged = true; |
150 |
} |
} |
151 |
|
|
152 |
/** |
/** |
|
* 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.handlerRelease = script.parserContext->eventHandlerByName("release"); |
|
|
script.handlerController = script.parserContext->eventHandlerByName("controller"); |
|
|
script.bHasValidScript = |
|
|
script.handlerInit || script.handlerNote || script.handlerRelease || |
|
|
script.handlerController; |
|
|
|
|
|
// amount of script handlers each script event has to execute |
|
|
int handlerExecCount = 0; |
|
|
if (script.handlerInit) handlerExecCount++; // "init" handler is always executed before the actual event handler |
|
|
if (script.handlerNote || script.handlerRelease || script.handlerController) // only one of these are executed after "init" handler |
|
|
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.handlerRelease = NULL; |
|
|
script.handlerController = NULL; |
|
|
} |
|
|
script.bHasValidScript = false; |
|
|
} |
|
|
|
|
|
/** |
|
153 |
* Implementation of virtual method from abstract EngineChannel interface. |
* Implementation of virtual method from abstract EngineChannel interface. |
154 |
* 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 |
155 |
* check if some engine channel parameter has changed since the last |
* check if some engine channel parameter has changed since the last |
383 |
event.Param.Note.Key = Key; |
event.Param.Note.Key = Key; |
384 |
event.Param.Note.Velocity = Velocity; |
event.Param.Note.Velocity = Velocity; |
385 |
event.Param.Note.Channel = MidiChannel; |
event.Param.Note.Channel = MidiChannel; |
386 |
|
memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes |
387 |
event.pEngineChannel = this; |
event.pEngineChannel = this; |
388 |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
389 |
else dmsg(1,("EngineChannel: Input event queue full!")); |
else dmsg(1,("EngineChannel: Input event queue full!")); |
425 |
event.Param.Note.Key = Key; |
event.Param.Note.Key = Key; |
426 |
event.Param.Note.Velocity = Velocity; |
event.Param.Note.Velocity = Velocity; |
427 |
event.Param.Note.Channel = MidiChannel; |
event.Param.Note.Channel = MidiChannel; |
428 |
|
memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes |
429 |
event.pEngineChannel = this; |
event.pEngineChannel = this; |
430 |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
431 |
else dmsg(1,("EngineChannel: Input event queue full!")); |
else dmsg(1,("EngineChannel: Input event queue full!")); |
462 |
event.Param.Note.Key = Key; |
event.Param.Note.Key = Key; |
463 |
event.Param.Note.Velocity = Velocity; |
event.Param.Note.Velocity = Velocity; |
464 |
event.Param.Note.Channel = MidiChannel; |
event.Param.Note.Channel = MidiChannel; |
465 |
|
memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes |
466 |
event.pEngineChannel = this; |
event.pEngineChannel = this; |
467 |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
468 |
else dmsg(1,("EngineChannel: Input event queue full!")); |
else dmsg(1,("EngineChannel: Input event queue full!")); |
504 |
event.Param.Note.Key = Key; |
event.Param.Note.Key = Key; |
505 |
event.Param.Note.Velocity = Velocity; |
event.Param.Note.Velocity = Velocity; |
506 |
event.Param.Note.Channel = MidiChannel; |
event.Param.Note.Channel = MidiChannel; |
507 |
|
memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes |
508 |
event.pEngineChannel = this; |
event.pEngineChannel = this; |
509 |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
510 |
else dmsg(1,("EngineChannel: Input event queue full!")); |
else dmsg(1,("EngineChannel: Input event queue full!")); |
539 |
event.Type = Event::type_pitchbend; |
event.Type = Event::type_pitchbend; |
540 |
event.Param.Pitch.Pitch = Pitch; |
event.Param.Pitch.Pitch = Pitch; |
541 |
event.Param.Pitch.Channel = MidiChannel; |
event.Param.Pitch.Channel = MidiChannel; |
542 |
|
memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes |
543 |
event.pEngineChannel = this; |
event.pEngineChannel = this; |
544 |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
545 |
else dmsg(1,("EngineChannel: Input event queue full!")); |
else dmsg(1,("EngineChannel: Input event queue full!")); |
569 |
event.Type = Event::type_pitchbend; |
event.Type = Event::type_pitchbend; |
570 |
event.Param.Pitch.Pitch = Pitch; |
event.Param.Pitch.Pitch = Pitch; |
571 |
event.Param.Pitch.Channel = MidiChannel; |
event.Param.Pitch.Channel = MidiChannel; |
572 |
|
memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes |
573 |
event.pEngineChannel = this; |
event.pEngineChannel = this; |
574 |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
575 |
else dmsg(1,("AbstractEngineChannel: Input event queue full!")); |
else dmsg(1,("AbstractEngineChannel: Input event queue full!")); |
596 |
event.Param.CC.Controller = Controller; |
event.Param.CC.Controller = Controller; |
597 |
event.Param.CC.Value = Value; |
event.Param.CC.Value = Value; |
598 |
event.Param.CC.Channel = MidiChannel; |
event.Param.CC.Channel = MidiChannel; |
599 |
|
memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes |
600 |
event.pEngineChannel = this; |
event.pEngineChannel = this; |
601 |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
602 |
else dmsg(1,("AbstractEngineChannel: Input event queue full!")); |
else dmsg(1,("AbstractEngineChannel: Input event queue full!")); |
628 |
event.Param.CC.Controller = Controller; |
event.Param.CC.Controller = Controller; |
629 |
event.Param.CC.Value = Value; |
event.Param.CC.Value = Value; |
630 |
event.Param.CC.Channel = MidiChannel; |
event.Param.CC.Channel = MidiChannel; |
631 |
|
memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes |
632 |
event.pEngineChannel = this; |
event.pEngineChannel = this; |
633 |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
634 |
else dmsg(1,("AbstractEngineChannel: Input event queue full!")); |
else dmsg(1,("AbstractEngineChannel: Input event queue full!")); |
643 |
|
|
644 |
Event event = pEngine->pEventGenerator->CreateEvent(); |
Event event = pEngine->pEventGenerator->CreateEvent(); |
645 |
event.Type = Event::type_channel_pressure; |
event.Type = Event::type_channel_pressure; |
646 |
|
event.Param.ChannelPressure.Controller = CTRL_TABLE_IDX_AFTERTOUCH; // required for instrument scripts |
647 |
event.Param.ChannelPressure.Value = Value; |
event.Param.ChannelPressure.Value = Value; |
648 |
event.Param.ChannelPressure.Channel = MidiChannel; |
event.Param.ChannelPressure.Channel = MidiChannel; |
649 |
|
memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes |
650 |
event.pEngineChannel = this; |
event.pEngineChannel = this; |
651 |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
652 |
else dmsg(1,("AbstractEngineChannel: Input event queue full!")); |
else dmsg(1,("AbstractEngineChannel: Input event queue full!")); |
661 |
|
|
662 |
Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos); |
Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos); |
663 |
event.Type = Event::type_channel_pressure; |
event.Type = Event::type_channel_pressure; |
664 |
|
event.Param.ChannelPressure.Controller = CTRL_TABLE_IDX_AFTERTOUCH; // required for instrument scripts |
665 |
event.Param.ChannelPressure.Value = Value; |
event.Param.ChannelPressure.Value = Value; |
666 |
event.Param.ChannelPressure.Channel = MidiChannel; |
event.Param.ChannelPressure.Channel = MidiChannel; |
667 |
|
memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes |
668 |
event.pEngineChannel = this; |
event.pEngineChannel = this; |
669 |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
670 |
else dmsg(1,("AbstractEngineChannel: Input event queue full!")); |
else dmsg(1,("AbstractEngineChannel: Input event queue full!")); |
682 |
event.Param.NotePressure.Key = Key; |
event.Param.NotePressure.Key = Key; |
683 |
event.Param.NotePressure.Value = Value; |
event.Param.NotePressure.Value = Value; |
684 |
event.Param.NotePressure.Channel = MidiChannel; |
event.Param.NotePressure.Channel = MidiChannel; |
685 |
|
memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes |
686 |
event.pEngineChannel = this; |
event.pEngineChannel = this; |
687 |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
688 |
else dmsg(1,("AbstractEngineChannel: Input event queue full!")); |
else dmsg(1,("AbstractEngineChannel: Input event queue full!")); |
700 |
event.Param.NotePressure.Key = Key; |
event.Param.NotePressure.Key = Key; |
701 |
event.Param.NotePressure.Value = Value; |
event.Param.NotePressure.Value = Value; |
702 |
event.Param.NotePressure.Channel = MidiChannel; |
event.Param.NotePressure.Channel = MidiChannel; |
703 |
|
memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes |
704 |
event.pEngineChannel = this; |
event.pEngineChannel = this; |
705 |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
706 |
else dmsg(1,("AbstractEngineChannel: Input event queue full!")); |
else dmsg(1,("AbstractEngineChannel: Input event queue full!")); |
778 |
<< devEvent.Type << "). This is a bug!"; |
<< devEvent.Type << "). This is a bug!"; |
779 |
continue; |
continue; |
780 |
} |
} |
781 |
|
memset(&event.Format, 0, sizeof(event.Format)); // init format specific stuff with zeroes |
782 |
event.pEngineChannel = this; |
event.pEngineChannel = this; |
783 |
// copy event to internal event list |
// copy event to internal event list |
784 |
if (pEvents->poolIsEmpty()) { |
if (pEvents->poolIsEmpty()) { |
814 |
} |
} |
815 |
eventQueueReader.free(); // free all copied events from input queue |
eventQueueReader.free(); // free all copied events from input queue |
816 |
} |
} |
817 |
|
|
818 |
/** |
/** |
819 |
* Called by real-time instrument script functions to schedule a new event |
* Called by real-time instrument script functions to schedule a new event |
820 |
* somewhere in future. |
* @a delay microseconds in future. |
821 |
|
* |
822 |
|
* @b IMPORTANT: for the supplied @a delay to be scheduled correctly, the |
823 |
|
* passed @a pEvent must be assigned a valid fragment time within the |
824 |
|
* current audio fragment boundaries. That fragment time will be used by |
825 |
|
* this method as basis for interpreting what "now" acutally is, and thus |
826 |
|
* it will be used as basis for calculating the precise scheduling time |
827 |
|
* for @a delay. The easiest way to achieve this is by copying a recent |
828 |
|
* event which happened within the current audio fragment cycle: i.e. the |
829 |
|
* original event which caused calling this method here. |
830 |
|
* |
831 |
|
* @param pEvent - event to be scheduled in future (event data will be copied) |
832 |
|
* @param delay - amount of microseconds in future (from now) when event shall be processed |
833 |
|
* @returns unique event ID of scheduled new event, or a negative number on error |
834 |
*/ |
*/ |
835 |
void AbstractEngineChannel::ScheduleEvent(const Event* pEvent, int delay) { //TODO: delay not implemented yet |
int AbstractEngineChannel::ScheduleEventMicroSec(const Event* pEvent, int delay) { |
836 |
// since delay is not implemented yet, we simply add the new event |
dmsg(3,("AbstractEngineChannel::ScheduleEventMicroSec(Event.Type=%d,delay=%d)\n", pEvent->Type, delay)); |
|
// to the event list of the current audio fragmet cycle for now |
|
837 |
RTList<Event>::Iterator itEvent = pEvents->allocAppend(); |
RTList<Event>::Iterator itEvent = pEvents->allocAppend(); |
838 |
if (itEvent) *itEvent = *pEvent; // copy event |
if (!itEvent) { |
839 |
|
dmsg(1,("AbstractEngineChannel::ScheduleEventMicroSec(): Event pool emtpy!\n")); |
840 |
|
return -1; |
841 |
|
} |
842 |
|
RTList<ScheduledEvent>::Iterator itNode = delayedEvents.schedulerNodes.allocAppend(); |
843 |
|
if (!itNode) { // scheduler node pool empty ... |
844 |
|
dmsg(1,("AbstractEngineChannel::ScheduleEventMicroSec(): ScheduledEvent pool empty!\n")); |
845 |
|
pEvents->free(itEvent); |
846 |
|
return -1; |
847 |
|
} |
848 |
|
// copy passed event |
849 |
|
*itEvent = *pEvent; |
850 |
|
// move copied event to list of delayed events |
851 |
|
itEvent = itEvent.moveToEndOf(delayedEvents.pList); |
852 |
|
// connect scheduler node with the copied event |
853 |
|
itNode->itEvent = itEvent; |
854 |
|
// add entry to time sorted scheduler queue for copied event |
855 |
|
pEngine->pEventGenerator->scheduleAheadMicroSec( |
856 |
|
delayedEvents.queue, *itNode, itEvent->FragmentPos(), delay |
857 |
|
); |
858 |
|
//dmsg(5,("ScheduledEvent queue size: %d\n", delayedEvents.queue.size())); |
859 |
|
return pEvents->getID(itEvent); |
860 |
|
} |
861 |
|
|
862 |
|
/** |
863 |
|
* Called by real-time instrument script functions to ignore the event |
864 |
|
* reflected by given event ID. The event will be freed immediately to its |
865 |
|
* pool and cannot be dereferenced by its old ID anymore. Even if its |
866 |
|
* allocated back from the Pool later on, it will have a different ID. |
867 |
|
*/ |
868 |
|
void AbstractEngineChannel::IgnoreEvent(int id) { |
869 |
|
RTList<Event>::Iterator it = pEvents->fromID(id); |
870 |
|
if (it) pEvents->free(it); |
871 |
} |
} |
872 |
|
|
873 |
FxSend* AbstractEngineChannel::AddFxSend(uint8_t MidiCtrl, String Name) throw (Exception) { |
FxSend* AbstractEngineChannel::AddFxSend(uint8_t MidiCtrl, String Name) throw (Exception) { |