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 * |
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; |
|
memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes |
|
386 |
event.pEngineChannel = this; |
event.pEngineChannel = this; |
387 |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
388 |
else dmsg(1,("EngineChannel: Input event queue full!")); |
else dmsg(1,("EngineChannel: Input event queue full!")); |
424 |
event.Param.Note.Key = Key; |
event.Param.Note.Key = Key; |
425 |
event.Param.Note.Velocity = Velocity; |
event.Param.Note.Velocity = Velocity; |
426 |
event.Param.Note.Channel = MidiChannel; |
event.Param.Note.Channel = MidiChannel; |
|
memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes |
|
427 |
event.pEngineChannel = this; |
event.pEngineChannel = this; |
428 |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
429 |
else dmsg(1,("EngineChannel: Input event queue full!")); |
else dmsg(1,("EngineChannel: Input event queue full!")); |
460 |
event.Param.Note.Key = Key; |
event.Param.Note.Key = Key; |
461 |
event.Param.Note.Velocity = Velocity; |
event.Param.Note.Velocity = Velocity; |
462 |
event.Param.Note.Channel = MidiChannel; |
event.Param.Note.Channel = MidiChannel; |
|
memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes |
|
463 |
event.pEngineChannel = this; |
event.pEngineChannel = this; |
464 |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
465 |
else dmsg(1,("EngineChannel: Input event queue full!")); |
else dmsg(1,("EngineChannel: Input event queue full!")); |
501 |
event.Param.Note.Key = Key; |
event.Param.Note.Key = Key; |
502 |
event.Param.Note.Velocity = Velocity; |
event.Param.Note.Velocity = Velocity; |
503 |
event.Param.Note.Channel = MidiChannel; |
event.Param.Note.Channel = MidiChannel; |
|
memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes |
|
504 |
event.pEngineChannel = this; |
event.pEngineChannel = this; |
505 |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
506 |
else dmsg(1,("EngineChannel: Input event queue full!")); |
else dmsg(1,("EngineChannel: Input event queue full!")); |
535 |
event.Type = Event::type_pitchbend; |
event.Type = Event::type_pitchbend; |
536 |
event.Param.Pitch.Pitch = Pitch; |
event.Param.Pitch.Pitch = Pitch; |
537 |
event.Param.Pitch.Channel = MidiChannel; |
event.Param.Pitch.Channel = MidiChannel; |
|
memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes |
|
538 |
event.pEngineChannel = this; |
event.pEngineChannel = this; |
539 |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
540 |
else dmsg(1,("EngineChannel: Input event queue full!")); |
else dmsg(1,("EngineChannel: Input event queue full!")); |
564 |
event.Type = Event::type_pitchbend; |
event.Type = Event::type_pitchbend; |
565 |
event.Param.Pitch.Pitch = Pitch; |
event.Param.Pitch.Pitch = Pitch; |
566 |
event.Param.Pitch.Channel = MidiChannel; |
event.Param.Pitch.Channel = MidiChannel; |
|
memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes |
|
567 |
event.pEngineChannel = this; |
event.pEngineChannel = this; |
568 |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
569 |
else dmsg(1,("AbstractEngineChannel: Input event queue full!")); |
else dmsg(1,("AbstractEngineChannel: Input event queue full!")); |
590 |
event.Param.CC.Controller = Controller; |
event.Param.CC.Controller = Controller; |
591 |
event.Param.CC.Value = Value; |
event.Param.CC.Value = Value; |
592 |
event.Param.CC.Channel = MidiChannel; |
event.Param.CC.Channel = MidiChannel; |
|
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; |
|
memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes |
|
624 |
event.pEngineChannel = this; |
event.pEngineChannel = this; |
625 |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
626 |
else dmsg(1,("AbstractEngineChannel: Input event queue full!")); |
else dmsg(1,("AbstractEngineChannel: Input event queue full!")); |
638 |
event.Param.ChannelPressure.Controller = CTRL_TABLE_IDX_AFTERTOUCH; // required for instrument scripts |
event.Param.ChannelPressure.Controller = CTRL_TABLE_IDX_AFTERTOUCH; // required for instrument scripts |
639 |
event.Param.ChannelPressure.Value = Value; |
event.Param.ChannelPressure.Value = Value; |
640 |
event.Param.ChannelPressure.Channel = MidiChannel; |
event.Param.ChannelPressure.Channel = MidiChannel; |
|
memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes |
|
641 |
event.pEngineChannel = this; |
event.pEngineChannel = this; |
642 |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
643 |
else dmsg(1,("AbstractEngineChannel: Input event queue full!")); |
else dmsg(1,("AbstractEngineChannel: Input event queue full!")); |
655 |
event.Param.ChannelPressure.Controller = CTRL_TABLE_IDX_AFTERTOUCH; // required for instrument scripts |
event.Param.ChannelPressure.Controller = CTRL_TABLE_IDX_AFTERTOUCH; // required for instrument scripts |
656 |
event.Param.ChannelPressure.Value = Value; |
event.Param.ChannelPressure.Value = Value; |
657 |
event.Param.ChannelPressure.Channel = MidiChannel; |
event.Param.ChannelPressure.Channel = MidiChannel; |
|
memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes |
|
658 |
event.pEngineChannel = this; |
event.pEngineChannel = this; |
659 |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
660 |
else dmsg(1,("AbstractEngineChannel: Input event queue full!")); |
else dmsg(1,("AbstractEngineChannel: Input event queue full!")); |
672 |
event.Param.NotePressure.Key = Key; |
event.Param.NotePressure.Key = Key; |
673 |
event.Param.NotePressure.Value = Value; |
event.Param.NotePressure.Value = Value; |
674 |
event.Param.NotePressure.Channel = MidiChannel; |
event.Param.NotePressure.Channel = MidiChannel; |
|
memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes |
|
675 |
event.pEngineChannel = this; |
event.pEngineChannel = this; |
676 |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
677 |
else dmsg(1,("AbstractEngineChannel: Input event queue full!")); |
else dmsg(1,("AbstractEngineChannel: Input event queue full!")); |
689 |
event.Param.NotePressure.Key = Key; |
event.Param.NotePressure.Key = Key; |
690 |
event.Param.NotePressure.Value = Value; |
event.Param.NotePressure.Value = Value; |
691 |
event.Param.NotePressure.Channel = MidiChannel; |
event.Param.NotePressure.Channel = MidiChannel; |
|
memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes |
|
692 |
event.pEngineChannel = this; |
event.pEngineChannel = this; |
693 |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
694 |
else dmsg(1,("AbstractEngineChannel: Input event queue full!")); |
else dmsg(1,("AbstractEngineChannel: Input event queue full!")); |
695 |
} |
} |
696 |
} |
} |
697 |
|
|
698 |
|
bool AbstractEngineChannel::applyTranspose(Event* event) { |
699 |
|
if (event->Type != Event::type_note_on && event->Type != Event::type_note_off) |
700 |
|
return true; // event OK (not a note event, nothing to do with it here) |
701 |
|
|
702 |
|
//HACK: we should better add the transpose value only to the most mandatory places (like for retrieving the region and calculating the tuning), because otherwise voices will unintendedly survive when changing transpose while playing |
703 |
|
const int k = event->Param.Note.Key + GlobalTranspose; |
704 |
|
if (k < 0 || k > 127) |
705 |
|
return false; // bad event, drop it |
706 |
|
|
707 |
|
event->Param.Note.Key = k; |
708 |
|
|
709 |
|
return true; // event OK |
710 |
|
} |
711 |
|
|
712 |
/** |
/** |
713 |
* Copy all events from the engine channel's input event queue buffer to |
* Copy all events from the engine channel's input event queue buffer to |
714 |
* the internal event list. This will be done at the beginning of each |
* the internal event list. This will be done at the beginning of each |
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 |
763 |
|
if (!applyTranspose(&*itEvent)) { |
764 |
|
// 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 |
769 |
|
if (!pEngine->LaunchNewNote(this, itEvent)) { |
770 |
|
// 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(&*itEvent)) { |
781 |
|
// 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 |
} |
} |
|
memset(&event.Format, 0, sizeof(event.Format)); // init format specific stuff with zeroes |
|
|
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 |
} |
} |
843 |
pEvent->ResetFragmentPos(); |
pEvent->ResetFragmentPos(); |
844 |
break; |
break; |
845 |
} |
} |
|
// copy event to internal event list |
|
846 |
if (pEvents->poolIsEmpty()) { |
if (pEvents->poolIsEmpty()) { |
847 |
dmsg(1,("Event pool emtpy!\n")); |
dmsg(1,("Event pool emtpy!\n")); |
848 |
break; |
break; |
849 |
} |
} |
850 |
*pEvents->allocAppend() = *pEvent; |
|
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 |
858 |
|
if (!applyTranspose(&*itEvent)) { |
859 |
|
// 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) |
864 |
|
if (itEvent->Type == Event::type_note_on) { |
865 |
|
if (!pEngine->LaunchNewNote(this, itEvent)) { |
866 |
|
// failed launching new note, so drop this event |
867 |
|
pEvents->free(itEvent); |
868 |
|
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 |
} |
} |
888 |
* |
* |
889 |
* @param pEvent - event to be scheduled in future (event data will be copied) |
* @param pEvent - event to be scheduled in future (event data will be copied) |
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 a negative number on error |
* @returns unique event ID of scheduled new event, or NULL on error |
892 |
*/ |
*/ |
893 |
int 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")); |
898 |
return -1; |
return 0; |
899 |
} |
} |
900 |
RTList<ScheduledEvent>::Iterator itNode = delayedEvents.schedulerNodes.allocAppend(); |
RTList<ScheduledEvent>::Iterator itNode = delayedEvents.schedulerNodes.allocAppend(); |
901 |
if (!itNode) { // scheduler node pool empty ... |
if (!itNode) { // scheduler node pool empty ... |
902 |
dmsg(1,("AbstractEngineChannel::ScheduleEventMicroSec(): ScheduledEvent pool empty!\n")); |
dmsg(1,("AbstractEngineChannel::ScheduleEventMicroSec(): ScheduledEvent pool empty!\n")); |
903 |
pEvents->free(itEvent); |
pEvents->free(itEvent); |
904 |
return -1; |
return 0; |
905 |
} |
} |
906 |
// copy passed event |
// copy passed event |
907 |
*itEvent = *pEvent; |
*itEvent = *pEvent; |
922 |
* reflected by given event ID. The event will be freed immediately to its |
* reflected by given event ID. The event will be freed immediately to its |
923 |
* pool and cannot be dereferenced by its old ID anymore. Even if its |
* pool and cannot be dereferenced by its old ID anymore. Even if its |
924 |
* allocated back from the Pool later on, it will have a different ID. |
* allocated back from the Pool later on, it will have a different ID. |
925 |
|
* |
926 |
|
* @param id - unique ID of event to be dropped |
927 |
*/ |
*/ |
928 |
void AbstractEngineChannel::IgnoreEvent(int id) { |
void AbstractEngineChannel::IgnoreEvent(event_id_t id) { |
929 |
RTList<Event>::Iterator it = pEvents->fromID(id); |
RTList<Event>::Iterator it = pEvents->fromID(id); |
930 |
if (it) pEvents->free(it); |
if (it) pEvents->free(it); |
931 |
} |
} |
932 |
|
|
933 |
|
/** @brief Drop the requested event. |
934 |
|
* |
935 |
|
* Called by real-time instrument script functions to ignore the event |
936 |
|
* reflected by the given event @a id. This method detects whether the |
937 |
|
* passed ID is actually a @c Note ID or a regular @c Event ID and act |
938 |
|
* accordingly. |
939 |
|
* |
940 |
|
* @param id - event id (from script scope) |
941 |
|
* @see ScriptID |
942 |
|
*/ |
943 |
|
void AbstractEngineChannel::IgnoreEventByScriptID(const ScriptID& id) { |
944 |
|
switch (id.type()) { |
945 |
|
case ScriptID::EVENT: |
946 |
|
IgnoreEvent( id.eventID() ); |
947 |
|
break; |
948 |
|
case ScriptID::NOTE: |
949 |
|
IgnoreNote( id.noteID() ); |
950 |
|
break; |
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) { |