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 |
|
pScript = NULL; |
51 |
} |
} |
52 |
|
|
53 |
AbstractEngineChannel::~AbstractEngineChannel() { |
AbstractEngineChannel::~AbstractEngineChannel() { |
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; |
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!")); |
635 |
} |
} |
636 |
} |
} |
637 |
|
|
638 |
|
void AbstractEngineChannel::SendChannelPressure(uint8_t Value, uint8_t MidiChannel) { |
639 |
|
if (pEngine) { |
640 |
|
// protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel |
641 |
|
LockGuard g; |
642 |
|
if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex); |
643 |
|
|
644 |
|
Event event = pEngine->pEventGenerator->CreateEvent(); |
645 |
|
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; |
648 |
|
event.Param.ChannelPressure.Channel = MidiChannel; |
649 |
|
memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes |
650 |
|
event.pEngineChannel = this; |
651 |
|
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
652 |
|
else dmsg(1,("AbstractEngineChannel: Input event queue full!")); |
653 |
|
} |
654 |
|
} |
655 |
|
|
656 |
|
void AbstractEngineChannel::SendChannelPressure(uint8_t Value, uint8_t MidiChannel, int32_t FragmentPos) { |
657 |
|
if (pEngine) { |
658 |
|
// protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel |
659 |
|
LockGuard g; |
660 |
|
if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex); |
661 |
|
|
662 |
|
Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos); |
663 |
|
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; |
666 |
|
event.Param.ChannelPressure.Channel = MidiChannel; |
667 |
|
memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes |
668 |
|
event.pEngineChannel = this; |
669 |
|
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
670 |
|
else dmsg(1,("AbstractEngineChannel: Input event queue full!")); |
671 |
|
} |
672 |
|
} |
673 |
|
|
674 |
|
void AbstractEngineChannel::SendPolyphonicKeyPressure(uint8_t Key, uint8_t Value, uint8_t MidiChannel) { |
675 |
|
if (pEngine) { |
676 |
|
// protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel |
677 |
|
LockGuard g; |
678 |
|
if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex); |
679 |
|
|
680 |
|
Event event = pEngine->pEventGenerator->CreateEvent(); |
681 |
|
event.Type = Event::type_note_pressure; |
682 |
|
event.Param.NotePressure.Key = Key; |
683 |
|
event.Param.NotePressure.Value = Value; |
684 |
|
event.Param.NotePressure.Channel = MidiChannel; |
685 |
|
memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes |
686 |
|
event.pEngineChannel = this; |
687 |
|
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
688 |
|
else dmsg(1,("AbstractEngineChannel: Input event queue full!")); |
689 |
|
} |
690 |
|
} |
691 |
|
|
692 |
|
void AbstractEngineChannel::SendPolyphonicKeyPressure(uint8_t Key, uint8_t Value, uint8_t MidiChannel, int32_t FragmentPos) { |
693 |
|
if (pEngine) { |
694 |
|
// protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel |
695 |
|
LockGuard g; |
696 |
|
if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex); |
697 |
|
|
698 |
|
Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos); |
699 |
|
event.Type = Event::type_note_pressure; |
700 |
|
event.Param.NotePressure.Key = Key; |
701 |
|
event.Param.NotePressure.Value = Value; |
702 |
|
event.Param.NotePressure.Channel = MidiChannel; |
703 |
|
memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes |
704 |
|
event.pEngineChannel = this; |
705 |
|
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
706 |
|
else dmsg(1,("AbstractEngineChannel: Input event queue full!")); |
707 |
|
} |
708 |
|
} |
709 |
|
|
710 |
/** |
/** |
711 |
* 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 |
712 |
* 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 |
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()) { |
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 |
820 |
|
* @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 |
|
int AbstractEngineChannel::ScheduleEventMicroSec(const Event* pEvent, int delay) { |
836 |
|
dmsg(3,("AbstractEngineChannel::ScheduleEventMicroSec(Event.Type=%d,delay=%d)\n", pEvent->Type, delay)); |
837 |
|
RTList<Event>::Iterator itEvent = pEvents->allocAppend(); |
838 |
|
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) { |
874 |
if (pEngine) pEngine->DisableAndLock(); |
if (pEngine) pEngine->DisableAndLock(); |
875 |
FxSend* pFxSend = new FxSend(this, MidiCtrl, Name); |
FxSend* pFxSend = new FxSend(this, MidiCtrl, Name); |