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) 2012-2016 Christian Schoenebeck and Andreas Persson * |
* Copyright (C) 2012-2017 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 * |
728 |
{ |
{ |
729 |
const uint8_t channel = MidiChannel() == midi_chan_all ? 0 : MidiChannel(); |
const uint8_t channel = MidiChannel() == midi_chan_all ? 0 : MidiChannel(); |
730 |
const int FragmentPos = 0; // randomly chosen, we don't care about jitter for virtual MIDI devices |
const int FragmentPos = 0; // randomly chosen, we don't care about jitter for virtual MIDI devices |
731 |
Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos); |
const Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos); |
732 |
VirtualMidiDevice::event_t devEvent; // the event format we get from the virtual MIDI device |
VirtualMidiDevice::event_t devEvent; // the event format we get from the virtual MIDI device |
733 |
// as we're going to (carefully) write some status to the |
// as we're going to (carefully) write some status to the |
734 |
// synchronized struct, we cast away the const |
// synchronized struct, we cast away the const |
739 |
VirtualMidiDevice* pDev = devices[i]; |
VirtualMidiDevice* pDev = devices[i]; |
740 |
// I think we can simply flush the whole FIFO(s), the user shouldn't be so fast ;-) |
// I think we can simply flush the whole FIFO(s), the user shouldn't be so fast ;-) |
741 |
while (pDev->GetMidiEventFromDevice(devEvent)) { |
while (pDev->GetMidiEventFromDevice(devEvent)) { |
742 |
|
if (pEvents->poolIsEmpty()) { |
743 |
|
dmsg(1,("Event pool emtpy!\n")); |
744 |
|
goto exitVirtualDevicesLoop; |
745 |
|
} |
746 |
|
|
747 |
|
// copy event to internal event list (this is already |
748 |
|
// required here, because the LaunchNewNote() call below |
749 |
|
// requires the event to be from the internal event pool for |
750 |
|
// being able to generate a valid event ID) |
751 |
|
RTList<Event>::Iterator itEvent = pEvents->allocAppend(); |
752 |
|
*itEvent = event; |
753 |
|
|
754 |
|
itEvent->pEngineChannel = this; |
755 |
|
|
756 |
switch (devEvent.Type) { |
switch (devEvent.Type) { |
757 |
case VirtualMidiDevice::EVENT_TYPE_NOTEON: |
case VirtualMidiDevice::EVENT_TYPE_NOTEON: |
758 |
event.Type = Event::type_note_on; |
itEvent->Type = Event::type_note_on; |
759 |
event.Param.Note.Key = devEvent.Arg1; |
itEvent->Param.Note.Key = devEvent.Arg1; |
760 |
event.Param.Note.Velocity = devEvent.Arg2; |
itEvent->Param.Note.Velocity = devEvent.Arg2; |
761 |
event.Param.Note.Channel = channel; |
itEvent->Param.Note.Channel = channel; |
762 |
// apply transpose setting to (note on/off) event |
// apply transpose setting to (note on/off) event |
763 |
if (!applyTranspose(&event)) |
if (!applyTranspose(&*itEvent)) { |
764 |
continue; // note value is out of range, so drop this event |
// note value is out of range, so drop this event |
765 |
|
pEvents->free(itEvent); |
766 |
|
continue; |
767 |
|
} |
768 |
// assign a new note to this note-on event |
// assign a new note to this note-on event |
769 |
if (!pEngine->LaunchNewNote(this, &event)) |
if (!pEngine->LaunchNewNote(this, itEvent)) { |
770 |
continue; // failed launching new note, so drop this event |
// failed launching new note, so drop this event |
771 |
|
pEvents->free(itEvent); |
772 |
|
continue; |
773 |
|
} |
774 |
break; |
break; |
775 |
case VirtualMidiDevice::EVENT_TYPE_NOTEOFF: |
case VirtualMidiDevice::EVENT_TYPE_NOTEOFF: |
776 |
event.Type = Event::type_note_off; |
itEvent->Type = Event::type_note_off; |
777 |
event.Param.Note.Key = devEvent.Arg1; |
itEvent->Param.Note.Key = devEvent.Arg1; |
778 |
event.Param.Note.Velocity = devEvent.Arg2; |
itEvent->Param.Note.Velocity = devEvent.Arg2; |
779 |
event.Param.Note.Channel = channel; |
itEvent->Param.Note.Channel = channel; |
780 |
if (!applyTranspose(&event)) |
if (!applyTranspose(&*itEvent)) { |
781 |
continue; // note value is out of range, so drop this event |
// note value is out of range, so drop this event |
782 |
|
pEvents->free(itEvent); |
783 |
|
continue; |
784 |
|
} |
785 |
break; |
break; |
786 |
case VirtualMidiDevice::EVENT_TYPE_CC: |
case VirtualMidiDevice::EVENT_TYPE_CC: |
787 |
switch (devEvent.Arg1) { |
switch (devEvent.Arg1) { |
788 |
case 0: // bank select MSB ... |
case 0: // bank select MSB ... |
789 |
SetMidiBankMsb(devEvent.Arg2); |
SetMidiBankMsb(devEvent.Arg2); |
790 |
continue; // don't push this event into FIFO |
// don't push this event into FIFO |
791 |
|
pEvents->free(itEvent); |
792 |
|
continue; |
793 |
case 32: // bank select LSB ... |
case 32: // bank select LSB ... |
794 |
SetMidiBankLsb(devEvent.Arg2); |
SetMidiBankLsb(devEvent.Arg2); |
795 |
continue; // don't push this event into FIFO |
// don't push this event into FIFO |
796 |
|
pEvents->free(itEvent); |
797 |
|
continue; |
798 |
default: // regular MIDI CC ... |
default: // regular MIDI CC ... |
799 |
event.Type = Event::type_control_change; |
itEvent->Type = Event::type_control_change; |
800 |
event.Param.CC.Controller = devEvent.Arg1; |
itEvent->Param.CC.Controller = devEvent.Arg1; |
801 |
event.Param.CC.Value = devEvent.Arg2; |
itEvent->Param.CC.Value = devEvent.Arg2; |
802 |
event.Param.CC.Channel = channel; |
itEvent->Param.CC.Channel = channel; |
803 |
} |
} |
804 |
break; |
break; |
805 |
case VirtualMidiDevice::EVENT_TYPE_PITCHBEND: |
case VirtualMidiDevice::EVENT_TYPE_PITCHBEND: |
806 |
event.Type = Event::type_pitchbend; |
itEvent->Type = Event::type_pitchbend; |
807 |
event.Param.Pitch.Pitch = int(devEvent.Arg2 << 7 | devEvent.Arg1) - 8192; |
itEvent->Param.Pitch.Pitch = int(devEvent.Arg2 << 7 | devEvent.Arg1) - 8192; |
808 |
event.Param.Pitch.Channel = channel; |
itEvent->Param.Pitch.Channel = channel; |
809 |
break; |
break; |
810 |
case VirtualMidiDevice::EVENT_TYPE_PROGRAM: |
case VirtualMidiDevice::EVENT_TYPE_PROGRAM: |
811 |
SendProgramChange(devEvent.Arg1); |
SendProgramChange(devEvent.Arg1); |
812 |
continue; // don't push this event into FIFO |
// don't push this event into FIFO |
813 |
|
pEvents->free(itEvent); |
814 |
|
continue; |
815 |
|
case VirtualMidiDevice::EVENT_TYPE_CHPRESSURE: |
816 |
|
itEvent->Type = Event::type_channel_pressure; |
817 |
|
itEvent->Param.ChannelPressure.Controller = CTRL_TABLE_IDX_AFTERTOUCH; |
818 |
|
itEvent->Param.ChannelPressure.Value = devEvent.Arg2; |
819 |
|
itEvent->Param.ChannelPressure.Channel = channel; |
820 |
|
break; |
821 |
default: |
default: |
822 |
std::cerr << "AbstractEngineChannel::ImportEvents() ERROR: unknown event type (" |
std::cerr << "AbstractEngineChannel::ImportEvents() ERROR: unknown event type (" |
823 |
<< devEvent.Type << "). This is a bug!"; |
<< devEvent.Type << "). This is a bug!"; |
824 |
|
pEvents->free(itEvent); // drop event |
825 |
continue; |
continue; |
826 |
} |
} |
|
event.pEngineChannel = this; |
|
|
// copy event to internal event list |
|
|
if (pEvents->poolIsEmpty()) { |
|
|
dmsg(1,("Event pool emtpy!\n")); |
|
|
goto exitVirtualDevicesLoop; |
|
|
} |
|
|
*pEvents->allocAppend() = event; |
|
827 |
} |
} |
828 |
} |
} |
829 |
} |
} |
847 |
dmsg(1,("Event pool emtpy!\n")); |
dmsg(1,("Event pool emtpy!\n")); |
848 |
break; |
break; |
849 |
} |
} |
850 |
|
|
851 |
|
// copy event to internal event list |
852 |
|
// (required already because LaunchNewNote() relies on it, see |
853 |
|
// comment about it above) |
854 |
|
RTList<Event>::Iterator itEvent = pEvents->allocAppend(); |
855 |
|
*itEvent = *pEvent; |
856 |
|
|
857 |
// apply transpose setting to (note on/off) event |
// apply transpose setting to (note on/off) event |
858 |
if (!applyTranspose(pEvent)) |
if (!applyTranspose(&*itEvent)) { |
859 |
continue; // it's a note event which has a note value out of range, so drop this event |
// it's a note event which has a note value out of range, so drop this event |
860 |
|
pEvents->free(itEvent); |
861 |
|
continue; |
862 |
|
} |
863 |
// assign a new note to this event (if its a note-on event) |
// assign a new note to this event (if its a note-on event) |
864 |
if (pEvent->Type == Event::type_note_on) |
if (itEvent->Type == Event::type_note_on) { |
865 |
if (!pEngine->LaunchNewNote(this, pEvent)) |
if (!pEngine->LaunchNewNote(this, itEvent)) { |
866 |
continue; // failed launching new note, so drop this event |
// failed launching new note, so drop this event |
867 |
// copy event to internal event list |
pEvents->free(itEvent); |
868 |
*pEvents->allocAppend() = *pEvent; |
continue; |
869 |
|
} |
870 |
|
} |
871 |
|
|
872 |
} |
} |
873 |
eventQueueReader.free(); // free all copied events from input queue |
eventQueueReader.free(); // free all copied events from input queue |
874 |
} |
} |
890 |
* @param delay - amount of microseconds in future (from now) when event shall be processed |
* @param delay - amount of microseconds in future (from now) when event shall be processed |
891 |
* @returns unique event ID of scheduled new event, or NULL on error |
* @returns unique event ID of scheduled new event, or NULL on error |
892 |
*/ |
*/ |
893 |
event_id_t AbstractEngineChannel::ScheduleEventMicroSec(const Event* pEvent, int delay) { |
event_id_t AbstractEngineChannel::ScheduleEventMicroSec(const Event* pEvent, int64_t delay) { |
894 |
dmsg(3,("AbstractEngineChannel::ScheduleEventMicroSec(Event.Type=%d,delay=%d)\n", pEvent->Type, delay)); |
dmsg(3,("AbstractEngineChannel::ScheduleEventMicroSec(Event.Type=%d,delay=%lld)\n", pEvent->Type, delay)); |
895 |
RTList<Event>::Iterator itEvent = pEvents->allocAppend(); |
RTList<Event>::Iterator itEvent = pEvents->allocAppend(); |
896 |
if (!itEvent) { |
if (!itEvent) { |
897 |
dmsg(1,("AbstractEngineChannel::ScheduleEventMicroSec(): Event pool emtpy!\n")); |
dmsg(1,("AbstractEngineChannel::ScheduleEventMicroSec(): Event pool emtpy!\n")); |
930 |
if (it) pEvents->free(it); |
if (it) pEvents->free(it); |
931 |
} |
} |
932 |
|
|
|
/** |
|
|
* Called by real-time instrument script functions to ignore the note |
|
|
* reflected by given note ID. The note's event will be freed immediately |
|
|
* to its event pool and this will prevent voices to be launched for the |
|
|
* note. |
|
|
* |
|
|
* NOTE: preventing a note by calling this method works only if the note |
|
|
* was launched within the current audio fragment cycle. |
|
|
* |
|
|
* @param id - unique ID of note to be dropped |
|
|
*/ |
|
|
void AbstractEngineChannel::IgnoreNote(note_id_t id) { |
|
|
NoteBase* pNote = pEngine->NoteByID(id); |
|
|
if (!pNote) return; |
|
|
IgnoreEvent(pNote->eventID); |
|
|
} |
|
|
|
|
933 |
/** @brief Drop the requested event. |
/** @brief Drop the requested event. |
934 |
* |
* |
935 |
* Called by real-time instrument script functions to ignore the event |
* Called by real-time instrument script functions to ignore the event |
951 |
} |
} |
952 |
} |
} |
953 |
|
|
954 |
|
/** @brief Order resuming of script execution instance "now". |
955 |
|
* |
956 |
|
* Called by real-time instrument script function stop_wait() to resume a |
957 |
|
* script callback currently being suspended (i.e. due to a wait() script |
958 |
|
* function call). |
959 |
|
* |
960 |
|
* @param itCallback - suspended script callback to be resumed |
961 |
|
* @param now - current scheduler time to be "now" |
962 |
|
* @param forever - whether this particulare script callback should ignore |
963 |
|
* all subsequent wait*() script function calls |
964 |
|
*/ |
965 |
|
void AbstractEngineChannel::ScheduleResumeOfScriptCallback(RTList<ScriptEvent>::Iterator& itCallback, sched_time_t now, bool forever) { |
966 |
|
// ignore if invalid iterator was passed |
967 |
|
if (!itCallback) return; |
968 |
|
|
969 |
|
ScriptEvent* pCallback = &*itCallback; |
970 |
|
|
971 |
|
// mark this callback to ignore all subsequent built-in wait*() script function calls |
972 |
|
if (forever) pCallback->ignoreAllWaitCalls = true; |
973 |
|
|
974 |
|
// ignore if callback is not in the scheduler queue |
975 |
|
if (pCallback->currentSchedulerQueue() != &pScript->suspendedEvents) return; |
976 |
|
|
977 |
|
// ignore if callback is already scheduled to be resumed "now" |
978 |
|
if (pCallback->scheduleTime <= now) return; |
979 |
|
|
980 |
|
// take it out from the scheduler queue and re-insert callback |
981 |
|
// to schedule the script callback for resuming execution "now" |
982 |
|
pScript->suspendedEvents.erase(*pCallback); |
983 |
|
pCallback->scheduleTime = now + 1; |
984 |
|
pScript->suspendedEvents.insert(*pCallback); |
985 |
|
} |
986 |
|
|
987 |
|
/** @brief Fork the given script execution instance. |
988 |
|
* |
989 |
|
* Called by real-time instrument script function fork() to create a new |
990 |
|
* script execution instance (child) of the script execution instance |
991 |
|
* (parent) that was calling fork(). This is essentially like creating a |
992 |
|
* new thread for a script handler being executing. The entire execution |
993 |
|
* state of parent is copied to the "forked" child. |
994 |
|
* |
995 |
|
* @param parent - original active script callback instance from which the |
996 |
|
* new child shall be forked from |
997 |
|
* @param bAutoAbort - whether the forked child shall automatically be |
998 |
|
* terminated as soon as parent terminates |
999 |
|
* @returns forked new child execution instance |
1000 |
|
*/ |
1001 |
|
RTList<ScriptEvent>::Iterator AbstractEngineChannel::forkScriptCallback(ScriptEvent* parent, bool bAutoAbort) { |
1002 |
|
// check if the max. amount of child forks for this parent event handler |
1003 |
|
// instance have not been exceeded yet |
1004 |
|
if (parent->countChildHandlers() >= MAX_FORK_PER_SCRIPT_HANDLER) |
1005 |
|
return RTList<ScriptEvent>::Iterator(); |
1006 |
|
|
1007 |
|
// allocate a new script callback instance for child to be forked |
1008 |
|
RTList<ScriptEvent>::Iterator itChild = pScript->pEvents->allocAppend(); |
1009 |
|
if (!itChild) return itChild; |
1010 |
|
|
1011 |
|
// copy entire script handler state from parent to forked child |
1012 |
|
parent->forkTo(&*itChild, bAutoAbort); |
1013 |
|
|
1014 |
|
// stick the parent ID and child ID respectively to each other |
1015 |
|
itChild->parentHandlerID = GetScriptCallbackID(parent); |
1016 |
|
parent->addChildHandlerID( GetScriptCallbackID(&*itChild) ); |
1017 |
|
|
1018 |
|
// insert newly created (forked) child event handler instance to the |
1019 |
|
// scheduler queue for being executed soon |
1020 |
|
pEngine->pEventGenerator->scheduleAheadMicroSec( |
1021 |
|
pScript->suspendedEvents, // scheduler queue |
1022 |
|
*itChild, // script event |
1023 |
|
parent->cause.FragmentPos(), // current time of script event (basis for its next execution) |
1024 |
|
0 // "resume" new child script callback instance ASAP |
1025 |
|
); |
1026 |
|
|
1027 |
|
return itChild; |
1028 |
|
} |
1029 |
|
|
1030 |
FxSend* AbstractEngineChannel::AddFxSend(uint8_t MidiCtrl, String Name) throw (Exception) { |
FxSend* AbstractEngineChannel::AddFxSend(uint8_t MidiCtrl, String Name) throw (Exception) { |
1031 |
if (pEngine) pEngine->DisableAndLock(); |
if (pEngine) pEngine->DisableAndLock(); |
1032 |
FxSend* pFxSend = new FxSend(this, MidiCtrl, Name); |
FxSend* pFxSend = new FxSend(this, MidiCtrl, Name); |
1054 |
} |
} |
1055 |
|
|
1056 |
uint AbstractEngineChannel::GetFxSendCount() { |
uint AbstractEngineChannel::GetFxSendCount() { |
1057 |
return fxSends.size(); |
return (uint)fxSends.size(); |
1058 |
} |
} |
1059 |
|
|
1060 |
void AbstractEngineChannel::RemoveFxSend(FxSend* pFxSend) { |
void AbstractEngineChannel::RemoveFxSend(FxSend* pFxSend) { |