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-2013 Christian Schoenebeck and Grigor Iliev * |
* Copyright (C) 2009-2015 Christian Schoenebeck and Grigor Iliev * |
8 |
* * |
* * |
9 |
* This program is free software; you can redistribute it and/or modify * |
* This program is free software; you can redistribute it and/or modify * |
10 |
* 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 * |
432 |
* @param pRegion - region the engine shall stop using |
* @param pRegion - region the engine shall stop using |
433 |
*/ |
*/ |
434 |
virtual void Suspend(RR* pRegion) { |
virtual void Suspend(RR* pRegion) { |
435 |
dmsg(2,("EngineBase: Suspending Region %x ...\n",pRegion)); |
dmsg(2,("EngineBase: Suspending Region %p ...\n",(void*)pRegion)); |
436 |
{ |
{ |
437 |
LockGuard lock(SuspendedRegionsMutex); |
LockGuard lock(SuspendedRegionsMutex); |
438 |
SuspensionChangeOngoing.Set(true); |
SuspensionChangeOngoing.Set(true); |
439 |
pPendingRegionSuspension = pRegion; |
pPendingRegionSuspension = pRegion; |
440 |
SuspensionChangeOngoing.WaitAndUnlockIf(true); |
SuspensionChangeOngoing.WaitAndUnlockIf(true); |
441 |
} |
} |
442 |
dmsg(2,("EngineBase: Region %x suspended.",pRegion)); |
dmsg(2,("EngineBase: Region %p suspended.",(void*)pRegion)); |
443 |
} |
} |
444 |
|
|
445 |
/** |
/** |
449 |
* @param pRegion - region the engine shall be allowed to use again |
* @param pRegion - region the engine shall be allowed to use again |
450 |
*/ |
*/ |
451 |
virtual void Resume(RR* pRegion) { |
virtual void Resume(RR* pRegion) { |
452 |
dmsg(2,("EngineBase: Resuming Region %x ...\n",pRegion)); |
dmsg(2,("EngineBase: Resuming Region %p ...\n",(void*)pRegion)); |
453 |
{ |
{ |
454 |
LockGuard lock(SuspendedRegionsMutex); |
LockGuard lock(SuspendedRegionsMutex); |
455 |
SuspensionChangeOngoing.Set(true); |
SuspensionChangeOngoing.Set(true); |
456 |
pPendingRegionResumption = pRegion; |
pPendingRegionResumption = pRegion; |
457 |
SuspensionChangeOngoing.WaitAndUnlockIf(true); |
SuspensionChangeOngoing.WaitAndUnlockIf(true); |
458 |
} |
} |
459 |
dmsg(2,("EngineBase: Region %x resumed.\n",pRegion)); |
dmsg(2,("EngineBase: Region %p resumed.\n",(void*)pRegion)); |
460 |
} |
} |
461 |
|
|
462 |
virtual void ResetSuspendedRegions() { |
virtual void ResetSuspendedRegions() { |
634 |
// if a valid real-time instrument script is loaded, pre-process |
// if a valid real-time instrument script is loaded, pre-process |
635 |
// the event list by running the script now, since the script |
// the event list by running the script now, since the script |
636 |
// might filter events or add new ones for this cycle |
// might filter events or add new ones for this cycle |
637 |
if (pChannel->script.bHasValidScript) { |
if (pChannel->pScript) { |
638 |
// resume any suspended script executions still hanging |
// resume any suspended script executions still hanging |
639 |
// around of previous audio fragment cycles |
// around of previous audio fragment cycles |
640 |
for (RTList<ScriptEvent>::Iterator itEvent = pChannel->pScriptEvents->first(), |
for (RTList<ScriptEvent>::Iterator itEvent = pChannel->pScript->pEvents->first(), |
641 |
end = pChannel->pScriptEvents->end(); itEvent != end; ++itEvent) |
end = pChannel->pScript->pEvents->end(); itEvent != end; ++itEvent) |
642 |
{ |
{ |
643 |
ResumeScriptEvent(pChannel, itEvent); //TODO: implement support for actual suspension time (i.e. passed to a script's wait() function call) |
ResumeScriptEvent(pChannel, itEvent); //TODO: implement support for actual suspension time (i.e. passed to a script's wait() function call) |
644 |
} |
} |
650 |
{ |
{ |
651 |
switch (itEvent->Type) { |
switch (itEvent->Type) { |
652 |
case Event::type_note_on: |
case Event::type_note_on: |
653 |
if (pChannel->script.handlerNote) |
if (pChannel->pScript->handlerNote) |
654 |
ProcessEventByScript(pChannel, itEvent, pChannel->script.handlerNote); |
ProcessEventByScript(pChannel, itEvent, pChannel->pScript->handlerNote); |
655 |
break; |
break; |
656 |
case Event::type_note_off: |
case Event::type_note_off: |
657 |
if (pChannel->script.handlerRelease) |
if (pChannel->pScript->handlerRelease) |
658 |
ProcessEventByScript(pChannel, itEvent, pChannel->script.handlerRelease); |
ProcessEventByScript(pChannel, itEvent, pChannel->pScript->handlerRelease); |
659 |
break; |
break; |
660 |
case Event::type_control_change: |
case Event::type_control_change: |
661 |
case Event::type_channel_pressure: |
case Event::type_channel_pressure: |
662 |
case Event::type_pitchbend: |
case Event::type_pitchbend: |
663 |
if (pChannel->script.handlerController) |
if (pChannel->pScript->handlerController) |
664 |
ProcessEventByScript(pChannel, itEvent, pChannel->script.handlerController); |
ProcessEventByScript(pChannel, itEvent, pChannel->pScript->handlerController); |
665 |
break; |
break; |
666 |
case Event::type_note_pressure: |
case Event::type_note_pressure: |
667 |
//TODO: ... |
//TODO: ... |
723 |
* @param pEventHandler - script's event handler to be executed |
* @param pEventHandler - script's event handler to be executed |
724 |
*/ |
*/ |
725 |
void ProcessEventByScript(AbstractEngineChannel* pChannel, RTList<Event>::Iterator& itEvent, VMEventHandler* pEventHandler) { |
void ProcessEventByScript(AbstractEngineChannel* pChannel, RTList<Event>::Iterator& itEvent, VMEventHandler* pEventHandler) { |
726 |
RTList<ScriptEvent>::Iterator itScriptEvent = |
const int key = itEvent->Param.Note.Key; // even if this is not a note on/off event, accessing it does not mean any harm |
727 |
pChannel->pScriptEvents->allocAppend(); |
// check if polyphonic data is passed from "note" to "release" |
728 |
|
// script event handlers |
729 |
|
if (pEventHandler == pChannel->pScript->handlerRelease && |
730 |
|
pChannel->pScript->handlerNote && |
731 |
|
pChannel->pScript->handlerNote->isPolyphonic() && |
732 |
|
pChannel->pScript->handlerRelease->isPolyphonic() && |
733 |
|
!pChannel->pScript->pKeyEvents[key]->isEmpty()) |
734 |
|
{ |
735 |
|
// polyphonic variable data is used/passed from "note" to |
736 |
|
// "release" script callback, so we have to recycle the |
737 |
|
// original "note on" script event(s) |
738 |
|
RTList<ScriptEvent>::Iterator it = pChannel->pScript->pKeyEvents[key]->first(); |
739 |
|
RTList<ScriptEvent>::Iterator end = pChannel->pScript->pKeyEvents[key]->end(); |
740 |
|
for (; it != end; ++it) { |
741 |
|
ProcessScriptEvent( |
742 |
|
pChannel, itEvent, pEventHandler, it |
743 |
|
); |
744 |
|
} |
745 |
|
} else { |
746 |
|
// no polyphonic data is used/passed from "note" to |
747 |
|
// "release" script callback, so just use a new fresh |
748 |
|
// script event object |
749 |
|
RTList<ScriptEvent>::Iterator itScriptEvent = |
750 |
|
pChannel->pScript->pEvents->allocAppend(); |
751 |
|
ProcessScriptEvent( |
752 |
|
pChannel, itEvent, pEventHandler, itScriptEvent |
753 |
|
); |
754 |
|
} |
755 |
|
} |
756 |
|
|
757 |
if (!itScriptEvent) return; // no free script event left for execution |
void ProcessScriptEvent(AbstractEngineChannel* pChannel, RTList<Event>::Iterator& itEvent, VMEventHandler* pEventHandler, RTList<ScriptEvent>::Iterator& itScriptEvent) { |
758 |
|
if (!itScriptEvent) return; // not a valid script event (i.e. because no free script event was left in the script event pool) |
759 |
|
|
760 |
// fill the list of script handlers to be executed by this event |
// fill the list of script handlers to be executed by this event |
761 |
int i = 0; |
int i = 0; |
|
if (pChannel->script.handlerInit) |
|
|
itScriptEvent->handlers[i++] = pChannel->script.handlerInit; |
|
762 |
itScriptEvent->handlers[i++] = pEventHandler; // actual event handler (i.e. note, controller) |
itScriptEvent->handlers[i++] = pEventHandler; // actual event handler (i.e. note, controller) |
763 |
itScriptEvent->handlers[i] = NULL; // NULL termination of list |
itScriptEvent->handlers[i] = NULL; // NULL termination of list |
764 |
|
|
765 |
// initialize/reset other members |
// initialize/reset other members |
766 |
itScriptEvent->cause = *itEvent; |
itScriptEvent->cause = *itEvent; |
767 |
|
itScriptEvent->id = pEventPool->getID(itEvent); |
768 |
itScriptEvent->currentHandler = 0; |
itScriptEvent->currentHandler = 0; |
769 |
itScriptEvent->executionSlices = 0; |
itScriptEvent->executionSlices = 0; |
770 |
|
|
771 |
// run script handler(s) |
// run script handler(s) |
772 |
VMExecStatus_t res = pScriptVM->exec( |
VMExecStatus_t res = pScriptVM->exec( |
773 |
pChannel->script.parserContext, &*itScriptEvent |
pChannel->pScript->parserContext, &*itScriptEvent |
774 |
); |
); |
775 |
|
|
776 |
// in case the script was suspended, keep it on the allocated |
// in case the script was suspended, keep it on the allocated |
777 |
// ScriptEvent list to be continued on the next audio cycle, |
// ScriptEvent list to be continued on the next audio cycle |
778 |
// otherwise if execution has been finished, free it for a new |
if (!(res & VM_EXEC_SUSPENDED)) { // script execution has finished without 'suspended' status ... |
779 |
// future script event to be triggered from start |
// if "polyphonic" variable data is passed from script's |
780 |
if (!(res & VM_EXEC_SUSPENDED)) |
// "note" event handler to its "release" event handler, then |
781 |
pChannel->pScriptEvents->free(itScriptEvent); |
// the script event must be kept and recycled for the later |
782 |
|
// occuring "release" script event ... |
783 |
|
if (pEventHandler == pChannel->pScript->handlerNote && |
784 |
|
pChannel->pScript->handlerRelease && |
785 |
|
pChannel->pScript->handlerNote->isPolyphonic() && |
786 |
|
pChannel->pScript->handlerRelease->isPolyphonic()) |
787 |
|
{ |
788 |
|
const int key = itEvent->Param.Note.Key; |
789 |
|
itScriptEvent.moveToEndOf(pChannel->pScript->pKeyEvents[key & 127]); |
790 |
|
} else { |
791 |
|
// ... otherwise if no polyphonic data is passed and |
792 |
|
// script's execution has finished without suspension |
793 |
|
// status, then free the script event for a new future |
794 |
|
// script event to be triggered from start |
795 |
|
pChannel->pScript->pEvents->free(itScriptEvent); |
796 |
|
} |
797 |
|
} |
798 |
} |
} |
799 |
|
|
800 |
/** @brief Resume execution of instrument script. |
/** @brief Resume execution of instrument script. |
815 |
* @param itScriptEvent - script execution that shall be resumed |
* @param itScriptEvent - script execution that shall be resumed |
816 |
*/ |
*/ |
817 |
void ResumeScriptEvent(AbstractEngineChannel* pChannel, RTList<ScriptEvent>::Iterator& itScriptEvent) { |
void ResumeScriptEvent(AbstractEngineChannel* pChannel, RTList<ScriptEvent>::Iterator& itScriptEvent) { |
818 |
|
VMEventHandler* handler = itScriptEvent->handlers[itScriptEvent->currentHandler]; |
819 |
|
|
820 |
// run script |
// run script |
821 |
VMExecStatus_t res = pScriptVM->exec( |
VMExecStatus_t res = pScriptVM->exec( |
822 |
pChannel->script.parserContext, &*itScriptEvent |
pChannel->pScript->parserContext, &*itScriptEvent |
823 |
); |
); |
824 |
// in case the script was again suspended, keep it on the allocated |
|
825 |
// ScriptEvent list to be continued on the next audio cycle, |
// in case the script was suspended, keep it on the allocated |
826 |
// otherwise if execution has been finished, free it for a new |
// ScriptEvent list to be continued on the next audio cycle |
827 |
// future script event to be triggered from start |
if (!(res & VM_EXEC_SUSPENDED)) { // script execution has finished without 'suspended' status ... |
828 |
if (!(res & VM_EXEC_SUSPENDED)) |
// if "polyphonic" variable data is passed from script's |
829 |
pChannel->pScriptEvents->free(itScriptEvent); |
// "note" event handler to its "release" event handler, then |
830 |
|
// the script event must be kept and recycled for the later |
831 |
|
// occuring "release" script event ... |
832 |
|
if (handler && handler == pChannel->pScript->handlerNote && |
833 |
|
pChannel->pScript->handlerRelease && |
834 |
|
pChannel->pScript->handlerNote->isPolyphonic() && |
835 |
|
pChannel->pScript->handlerRelease->isPolyphonic()) |
836 |
|
{ |
837 |
|
const int key = itScriptEvent->cause.Param.Note.Key; |
838 |
|
itScriptEvent.moveToEndOf(pChannel->pScript->pKeyEvents[key & 127]); |
839 |
|
} else { |
840 |
|
// ... otherwise if no polyphonic data is passed and |
841 |
|
// script's execution has finished without suspension |
842 |
|
// status, then free the script event for a new future |
843 |
|
// script event to be triggered from start |
844 |
|
pChannel->pScript->pEvents->free(itScriptEvent); |
845 |
|
} |
846 |
|
} |
847 |
} |
} |
848 |
|
|
849 |
/** |
/** |
978 |
dmsg(5,("Engine: instrument change command received\n")); |
dmsg(5,("Engine: instrument change command received\n")); |
979 |
cmd.bChangeInstrument = false; |
cmd.bChangeInstrument = false; |
980 |
pEngineChannel->pInstrument = cmd.pInstrument; |
pEngineChannel->pInstrument = cmd.pInstrument; |
981 |
|
pEngineChannel->pScript = |
982 |
|
cmd.pScript->bHasValidScript ? cmd.pScript : NULL; |
983 |
instrumentChanged = true; |
instrumentChanged = true; |
984 |
|
|
985 |
pEngineChannel->MarkAllActiveVoicesAsOrphans(); |
pEngineChannel->MarkAllActiveVoicesAsOrphans(); |
986 |
|
|
987 |
|
// the script's "init" event handler is only executed |
988 |
|
// once (when the script is loaded or reloaded) |
989 |
|
if (pEngineChannel->pScript && pEngineChannel->pScript->handlerInit) { |
990 |
|
RTList<ScriptEvent>::Iterator itScriptEvent = |
991 |
|
pEngineChannel->pScript->pEvents->allocAppend(); |
992 |
|
|
993 |
|
itScriptEvent->cause.pEngineChannel = pEngineChannel; |
994 |
|
itScriptEvent->handlers[0] = pEngineChannel->pScript->handlerInit; |
995 |
|
itScriptEvent->handlers[1] = NULL; |
996 |
|
|
997 |
|
VMExecStatus_t res = pScriptVM->exec( |
998 |
|
pEngineChannel->pScript->parserContext, &*itScriptEvent |
999 |
|
); |
1000 |
|
|
1001 |
|
pEngineChannel->pScript->pEvents->free(itScriptEvent); |
1002 |
|
} |
1003 |
} |
} |
1004 |
} |
} |
1005 |
|
|
1155 |
case 0x1d: { // reverb send of note (Roland GS NRPN) |
case 0x1d: { // reverb send of note (Roland GS NRPN) |
1156 |
const uint note = NrpnCtrlLSB; |
const uint note = NrpnCtrlLSB; |
1157 |
const float reverb = float(itControlChangeEvent->Param.CC.Value) / 127.0f; |
const float reverb = float(itControlChangeEvent->Param.CC.Value) / 127.0f; |
1158 |
dmsg(4,("Note Reverb Send NRPN received (note=%d,send=%d).\n", note, reverb)); |
dmsg(4,("Note Reverb Send NRPN received (note=%d,send=%f).\n", note, reverb)); |
1159 |
if (note < 128) |
if (note < 128) |
1160 |
pChannel->pMIDIKeyInfo[note].ReverbSend = reverb; |
pChannel->pMIDIKeyInfo[note].ReverbSend = reverb; |
1161 |
break; |
break; |
1163 |
case 0x1e: { // chorus send of note (Roland GS NRPN) |
case 0x1e: { // chorus send of note (Roland GS NRPN) |
1164 |
const uint note = NrpnCtrlLSB; |
const uint note = NrpnCtrlLSB; |
1165 |
const float chorus = float(itControlChangeEvent->Param.CC.Value) / 127.0f; |
const float chorus = float(itControlChangeEvent->Param.CC.Value) / 127.0f; |
1166 |
dmsg(4,("Note Chorus Send NRPN received (note=%d,send=%d).\n", note, chorus)); |
dmsg(4,("Note Chorus Send NRPN received (note=%d,send=%f).\n", note, chorus)); |
1167 |
if (note < 128) |
if (note < 128) |
1168 |
pChannel->pMIDIKeyInfo[note].ChorusSend = chorus; |
pChannel->pMIDIKeyInfo[note].ChorusSend = chorus; |
1169 |
break; |
break; |
1377 |
pChannel->ProcessKeySwitchChange(key); |
pChannel->ProcessKeySwitchChange(key); |
1378 |
|
|
1379 |
pKey->KeyPressed = true; // the MIDI key was now pressed down |
pKey->KeyPressed = true; // the MIDI key was now pressed down |
1380 |
|
pChannel->KeyDown[key] = true; // just used as built-in %KEY_DOWN script variable |
1381 |
pKey->Velocity = itNoteOnEventOnKeyList->Param.Note.Velocity; |
pKey->Velocity = itNoteOnEventOnKeyList->Param.Note.Velocity; |
1382 |
pKey->NoteOnTime = FrameTime + itNoteOnEventOnKeyList->FragmentPos(); // will be used to calculate note length |
pKey->NoteOnTime = FrameTime + itNoteOnEventOnKeyList->FragmentPos(); // will be used to calculate note length |
1383 |
|
|
1454 |
#endif |
#endif |
1455 |
|
|
1456 |
pKey->KeyPressed = false; // the MIDI key was now released |
pKey->KeyPressed = false; // the MIDI key was now released |
1457 |
|
pChannel->KeyDown[iKey] = false; // just used as built-in %KEY_DOWN script variable |
1458 |
|
|
1459 |
// move event to the key's own event list |
// move event to the key's own event list |
1460 |
RTList<Event>::Iterator itNoteOffEventOnKeyList = itNoteOffEvent.moveToEndOf(pKey->pEvents); |
RTList<Event>::Iterator itNoteOffEventOnKeyList = itNoteOffEvent.moveToEndOf(pKey->pEvents); |