4 |
* * |
* * |
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-2015 Christian Schoenebeck and Grigor Iliev * |
* Copyright (C) 2009-2012 Christian Schoenebeck and Grigor Iliev * |
8 |
|
* 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 * |
636 |
// the event list by running the script now, since the script |
// the event list by running the script now, since the script |
637 |
// might filter events or add new ones for this cycle |
// might filter events or add new ones for this cycle |
638 |
if (pChannel->pScript) { |
if (pChannel->pScript) { |
639 |
// resume any suspended script executions still hanging |
const sched_time_t fragmentEndTime = pEventGenerator->schedTimeAtCurrentFragmentEnd(); |
640 |
// around of previous audio fragment cycles |
|
641 |
for (RTList<ScriptEvent>::Iterator itEvent = pChannel->pScript->pEvents->first(), |
// resume suspended script executions been scheduled for |
642 |
end = pChannel->pScript->pEvents->end(); itEvent != end; ++itEvent) |
// this audio fragment cycle (which were suspended in a |
643 |
{ |
// previous audio fragment cycle) |
644 |
ResumeScriptEvent(pChannel, itEvent); //TODO: implement support for actual suspension time (i.e. passed to a script's wait() function call) |
ProcessSuspendedScriptEvents(pChannel, fragmentEndTime); |
|
} |
|
645 |
|
|
646 |
// spawn new script executions for the new MIDI events of |
// spawn new script executions for the new MIDI events of |
647 |
// this audio fragment cycle |
// this audio fragment cycle |
648 |
|
// |
649 |
|
// FIXME: it would probably be better to just schedule newly spawned script executions here and then execute them altogether with already suspended ones all at once in order of all their scheduled timing |
650 |
for (RTList<Event>::Iterator itEvent = pChannel->pEvents->first(), |
for (RTList<Event>::Iterator itEvent = pChannel->pEvents->first(), |
651 |
end = pChannel->pEvents->end(); itEvent != end; ++itEvent) |
end = pChannel->pEvents->end(); itEvent != end; ++itEvent) |
652 |
{ |
{ |
670 |
break; |
break; |
671 |
} |
} |
672 |
} |
} |
673 |
|
|
674 |
|
// this has to be run again, since the newly spawned scripts |
675 |
|
// above may have cause suspended scripts that must be |
676 |
|
// resumed within this same audio fragment cycle |
677 |
|
// |
678 |
|
// FIXME: see FIXME comment above |
679 |
|
ProcessSuspendedScriptEvents(pChannel, fragmentEndTime); |
680 |
|
} |
681 |
|
|
682 |
|
// if there are any delayed events scheduled for the current |
683 |
|
// audio fragment cycle, then move and sort them into the main |
684 |
|
// event list |
685 |
|
if (!pChannel->delayedEvents.queue.isEmpty()) { |
686 |
|
dmsg(5,("Engine: There are delayed MIDI events (total queue size: %d) ...\n", pChannel->delayedEvents.queue.size())); |
687 |
|
const sched_time_t fragmentEndTime = pEventGenerator->schedTimeAtCurrentFragmentEnd(); |
688 |
|
RTList<Event>::Iterator itEvent = pChannel->pEvents->first(); |
689 |
|
while (true) { |
690 |
|
RTList<ScheduledEvent>::Iterator itDelayedEventNode = |
691 |
|
pEventGenerator->popNextScheduledEvent( |
692 |
|
pChannel->delayedEvents.queue, |
693 |
|
pChannel->delayedEvents.schedulerNodes, |
694 |
|
fragmentEndTime |
695 |
|
); |
696 |
|
if (!itDelayedEventNode) break; |
697 |
|
// get the actual delayed event object and free the used scheduler node |
698 |
|
RTList<Event>::Iterator itDelayedEvent = itDelayedEventNode->itEvent; |
699 |
|
pChannel->delayedEvents.schedulerNodes.free(itDelayedEventNode); |
700 |
|
if (!itDelayedEvent) { // should never happen, but just to be sure ... |
701 |
|
dmsg(1,("Engine: Oops, invalid delayed event!\n")); |
702 |
|
continue; |
703 |
|
} |
704 |
|
// skip all events on main event list which have a time |
705 |
|
// before (or equal to) the delayed event to be inserted |
706 |
|
for (; itEvent && itEvent->FragmentPos() <= itDelayedEvent->FragmentPos(); |
707 |
|
++itEvent); |
708 |
|
// now move delayed event from delayedEvents.pList to |
709 |
|
// the current position on the main event list |
710 |
|
itEvent = itDelayedEvent.moveBefore(itEvent); |
711 |
|
dmsg(5,("Engine: Inserted event of type %d into main event list (queue size: %d).\n", itEvent->Type, pChannel->delayedEvents.queue.size())); |
712 |
|
} |
713 |
|
dmsg(5,("Engine: End of delayed events (total queue size: %d).\n", pChannel->delayedEvents.queue.size())); |
714 |
} |
} |
715 |
|
|
716 |
// now process all events regularly |
// now process all events regularly |
755 |
pLastStolenChannel = NULL; |
pLastStolenChannel = NULL; |
756 |
} |
} |
757 |
|
|
758 |
|
/** |
759 |
|
* Run all suspended script execution instances which are scheduled |
760 |
|
* to be resumed for the current audio fragment cycle. |
761 |
|
* |
762 |
|
* @param pChannel - engine channel on which suspended events occurred |
763 |
|
*/ |
764 |
|
void ProcessSuspendedScriptEvents(AbstractEngineChannel* pChannel, const sched_time_t fragmentEndTime) { |
765 |
|
while (true) { |
766 |
|
RTList<ScriptEvent>::Iterator itEvent = |
767 |
|
pEventGenerator->popNextScheduledScriptEvent( |
768 |
|
pChannel->pScript->suspendedEvents, |
769 |
|
*pChannel->pScript->pEvents, fragmentEndTime |
770 |
|
); |
771 |
|
if (!itEvent) break; |
772 |
|
ResumeScriptEvent(pChannel, itEvent); |
773 |
|
} |
774 |
|
} |
775 |
|
|
776 |
/** @brief Call instrument script's event handler for this event. |
/** @brief Call instrument script's event handler for this event. |
777 |
* |
* |
778 |
* Causes a new execution instance of the currently loaded real-time |
* Causes a new execution instance of the currently loaded real-time |
779 |
* instrument script's event handler (callback) to be spawned for |
* instrument script's event handler (callback) to be spawned for |
780 |
* the given MIDI event. |
* the given MIDI event. |
781 |
* |
* |
782 |
* @param pChannel - engine channel on which the MIDI event occured |
* @param pChannel - engine channel on which the MIDI event occurred |
783 |
* @param itEvent - MIDI event that causes this new script execution |
* @param itEvent - MIDI event that causes this new script execution |
784 |
* @param pEventHandler - script's event handler to be executed |
* @param pEventHandler - script's event handler to be executed |
785 |
*/ |
*/ |
815 |
} |
} |
816 |
} |
} |
817 |
|
|
818 |
|
/** @brief Spawn new execution instance of an instrument script handler. |
819 |
|
* |
820 |
|
* Will be called to initiate a new execution of a real-time |
821 |
|
* instrument script event right from the start of the script's |
822 |
|
* respective handler. If script execution did not complete after |
823 |
|
* calling this method, the respective script exeuction is then |
824 |
|
* suspended and a call to ResumeScriptEvent() will be used next |
825 |
|
* time to continue its execution. |
826 |
|
* |
827 |
|
* @param pChannel - engine channel this script is running for |
828 |
|
* @param itEvent - event which caused execution of this script |
829 |
|
* event handler |
830 |
|
* @param pEventHandler - VM representation of event handler to be |
831 |
|
* executed |
832 |
|
* @param itScriptEvent - script event that shall be processed |
833 |
|
*/ |
834 |
void ProcessScriptEvent(AbstractEngineChannel* pChannel, RTList<Event>::Iterator& itEvent, VMEventHandler* pEventHandler, RTList<ScriptEvent>::Iterator& itScriptEvent) { |
void ProcessScriptEvent(AbstractEngineChannel* pChannel, RTList<Event>::Iterator& itEvent, VMEventHandler* pEventHandler, RTList<ScriptEvent>::Iterator& itScriptEvent) { |
835 |
if (!itScriptEvent) return; // not a valid script event (i.e. because no free script event was left in the script event pool) |
if (!itScriptEvent) return; // not a valid script event (i.e. because no free script event was left in the script event pool) |
836 |
|
|
850 |
pChannel->pScript->parserContext, &*itScriptEvent |
pChannel->pScript->parserContext, &*itScriptEvent |
851 |
); |
); |
852 |
|
|
853 |
// in case the script was suspended, keep it on the allocated |
// was the script suspended? |
854 |
// ScriptEvent list to be continued on the next audio cycle |
if (res & VM_EXEC_SUSPENDED) { // script was suspended ... |
855 |
if (!(res & VM_EXEC_SUSPENDED)) { // script execution has finished without 'suspended' status ... |
// in case the script was suspended, keep it on the allocated |
856 |
|
// ScriptEvent list to be resume at the scheduled time in future, |
857 |
|
// additionally insert it into a sorted time queue |
858 |
|
pEventGenerator->scheduleAheadMicroSec( |
859 |
|
pChannel->pScript->suspendedEvents, // scheduler queue |
860 |
|
*itScriptEvent, // script event |
861 |
|
itScriptEvent->cause.FragmentPos(), // current time of script event (basis for its next execution) |
862 |
|
itScriptEvent->execCtx->suspensionTimeMicroseconds() // how long shall it be suspended |
863 |
|
); |
864 |
|
} else { // script execution has finished without 'suspended' status ... |
865 |
// if "polyphonic" variable data is passed from script's |
// if "polyphonic" variable data is passed from script's |
866 |
// "note" event handler to its "release" event handler, then |
// "note" event handler to its "release" event handler, then |
867 |
// the script event must be kept and recycled for the later |
// the script event must be kept and recycled for the later |
908 |
pChannel->pScript->parserContext, &*itScriptEvent |
pChannel->pScript->parserContext, &*itScriptEvent |
909 |
); |
); |
910 |
|
|
911 |
// in case the script was suspended, keep it on the allocated |
// was the script suspended? |
912 |
// ScriptEvent list to be continued on the next audio cycle |
if (res & VM_EXEC_SUSPENDED) { |
913 |
if (!(res & VM_EXEC_SUSPENDED)) { // script execution has finished without 'suspended' status ... |
// in case the script was suspended, keep it on the allocated |
914 |
|
// ScriptEvent list to be resume at the scheduled time in future, |
915 |
|
// additionally insert it into a sorted time queue |
916 |
|
pEventGenerator->scheduleAheadMicroSec( |
917 |
|
pChannel->pScript->suspendedEvents, // scheduler queue |
918 |
|
*itScriptEvent, // script event |
919 |
|
itScriptEvent->cause.FragmentPos(), // current time of script event (basis for its next execution) |
920 |
|
itScriptEvent->execCtx->suspensionTimeMicroseconds() // how long shall it be suspended |
921 |
|
); |
922 |
|
} else { // script execution has finished without 'suspended' status ... |
923 |
// if "polyphonic" variable data is passed from script's |
// if "polyphonic" variable data is passed from script's |
924 |
// "note" event handler to its "release" event handler, then |
// "note" event handler to its "release" event handler, then |
925 |
// the script event must be kept and recycled for the later |
// the script event must be kept and recycled for the later |
947 |
* voice stealing and postpone the note-on event until the selected |
* voice stealing and postpone the note-on event until the selected |
948 |
* voice actually died. |
* voice actually died. |
949 |
* |
* |
950 |
* @param pEngineChannel - engine channel on which this event occured on |
* @param pEngineChannel - engine channel on which this event occurred on |
951 |
* @param itNoteOnEvent - key, velocity and time stamp of the event |
* @param itNoteOnEvent - key, velocity and time stamp of the event |
952 |
* @returns 0 on success, a value < 0 if no active voice could be picked for voice stealing |
* @returns 0 on success, a value < 0 if no active voice could be picked for voice stealing |
953 |
*/ |
*/ |
1185 |
pChannel->FreeAllInactiveKyes(); |
pChannel->FreeAllInactiveKyes(); |
1186 |
|
|
1187 |
// empty the engine channel's own event lists |
// empty the engine channel's own event lists |
1188 |
pChannel->ClearEventLists(); |
// (only events of the current audio fragment cycle) |
1189 |
|
pChannel->ClearEventListsOfCurrentFragment(); |
1190 |
} |
} |
1191 |
|
|
1192 |
/** |
/** |
1412 |
/** |
/** |
1413 |
* Assigns and triggers a new voice for the respective MIDI key. |
* Assigns and triggers a new voice for the respective MIDI key. |
1414 |
* |
* |
1415 |
* @param pEngineChannel - engine channel on which this event occured on |
* @param pEngineChannel - engine channel on which this event occurred on |
1416 |
* @param itNoteOnEvent - key, velocity and time stamp of the event |
* @param itNoteOnEvent - key, velocity and time stamp of the event |
1417 |
*/ |
*/ |
1418 |
virtual void ProcessNoteOn(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNoteOnEvent) { |
virtual void ProcessNoteOn(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNoteOnEvent) { |
1524 |
* sustain pedal will be released or voice turned inactive by itself (e.g. |
* sustain pedal will be released or voice turned inactive by itself (e.g. |
1525 |
* due to completion of sample playback). |
* due to completion of sample playback). |
1526 |
* |
* |
1527 |
* @param pEngineChannel - engine channel on which this event occured on |
* @param pEngineChannel - engine channel on which this event occurred on |
1528 |
* @param itNoteOffEvent - key, velocity and time stamp of the event |
* @param itNoteOffEvent - key, velocity and time stamp of the event |
1529 |
*/ |
*/ |
1530 |
virtual void ProcessNoteOff(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNoteOffEvent) { |
virtual void ProcessNoteOff(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNoteOffEvent) { |
1652 |
} |
} |
1653 |
pVoicePool->clear(); |
pVoicePool->clear(); |
1654 |
|
|
1655 |
|
// reset all engine channels |
1656 |
|
for (int i = 0; i < engineChannels.size(); i++) { |
1657 |
|
AbstractEngineChannel* pEngineChannel = |
1658 |
|
static_cast<AbstractEngineChannel*>(engineChannels[i]); |
1659 |
|
pEngineChannel->ResetInternal(false/*don't reset engine*/); |
1660 |
|
} |
1661 |
|
|
1662 |
// reset disk thread |
// reset disk thread |
1663 |
if (pDiskThread) pDiskThread->Reset(); |
if (pDiskThread) pDiskThread->Reset(); |
1664 |
|
|
1687 |
* called by the ProcessNoteOn() method and by the voices itself |
* called by the ProcessNoteOn() method and by the voices itself |
1688 |
* (e.g. to spawn further voices on the same key for layered sounds). |
* (e.g. to spawn further voices on the same key for layered sounds). |
1689 |
* |
* |
1690 |
* @param pEngineChannel - engine channel on which this event occured on |
* @param pEngineChannel - engine channel on which this event occurred on |
1691 |
* @param itNoteOnEvent - key, velocity and time stamp of the event |
* @param itNoteOnEvent - key, velocity and time stamp of the event |
1692 |
* @param iLayer - layer index for the new voice (optional - only |
* @param iLayer - layer index for the new voice (optional - only |
1693 |
* in case of layered sounds of course) |
* in case of layered sounds of course) |