--- linuxsampler/trunk/src/engines/common/MidiKeyboardManager.h 2010/08/10 12:05:19 2114 +++ linuxsampler/trunk/src/engines/common/MidiKeyboardManager.h 2018/12/23 20:08:41 3445 @@ -30,6 +30,7 @@ #include "../../EventListeners.h" #include "../../common/Pool.h" #include "../../common/global_private.h" +#include "Note.h" namespace LinuxSampler { @@ -97,8 +98,56 @@ virtual void PostProcessSostenutoPedalDown() { } }; + /** + * This is the base class for class MidiKeyboardManager::MidiKey. It is + * not intended to be instantiated directly. Instead it just defines + * the part of class MidiKey which is not dependant on a C++ template + * parameter. + * + * There are also ScriptEvent lists maintained for each key, which are not + * stored here though, but on the InstrumentScript structure. Simply because + * RTLists are tied to one Pool instance, and it would be error prone to + * maintain @c Pool and @c RTList separately, + * since one would need to be very careful to reallocate the lists when the + * script was changed or when the Engine instance changed, etc. + * + * @see InstrumentScript::pKeyEvents + */ + class MidiKeyBase { + public: + bool KeyPressed; ///< Is true if the respective MIDI key is currently pressed. + bool Active; ///< If the key contains active voices. + release_trigger_t ReleaseTrigger; ///< If we have to launch release triggered voice(s) when either the key or sustain pedal is released. + Pool::Iterator itSelf; ///< hack to allow fast deallocation of the key from the list of active keys + RTList* pEvents; ///< Key specific events (only Note-on, Note-off and sustain pedal currently) + int VoiceTheftsQueued; ///< Amount of voices postponed due to shortage of voices. + uint32_t* pRoundRobinIndex; ///< For the round robin dimension: current articulation for this key, will be incremented for each note on + uint8_t Velocity; ///< Latest Note-on velocity for this key + unsigned long NoteOnTime; ///< Time for latest Note-on event for this key + float Volume; ///< Individual volume level for this MIDI key (usually 1.0f unless Roland GS NRPN 0x1Ann was received, nn reflecting the note number, see EngineBase::ProcessHardcodedControllers()) + float PanLeft; ///< Individual volume balance (left channel coefficient) for this MIDI key (usually 1.0f unless Roland GS NRPN 0x1Cnn was received, nn reflecting the note number, see EngineBase::ProcessHardcodedControllers()) + float PanRight; ///< Individual volume balance (right channel coefficient) for this MIDI key (usually 1.0f unless Roland GS NRPN 0x1Cnn was received, nn reflecting the note number, see EngineBase::ProcessHardcodedControllers()) + optional ReverbSend; ///< Optional individual reverb send level for this MIDI key (usually not set, unless Roland GS NRPN 0x1Dnn was received, nn reflecting the note number, see EngineBase::ProcessHardcodedControllers()) + optional ChorusSend; ///< Optional individual chorus send level for this MIDI key (usually not set, unless Roland GS NRPN 0x1Enn was received, nn reflecting the note number, see EngineBase::ProcessHardcodedControllers()) + }; + + class MidiKeyboardManagerBase { + public: + Pool* pActiveKeys; ///< Holds all keys in it's allocation list with active voices. + bool SoloMode; ///< in Solo Mode we only play one voice (group) at a time + int SoloKey; ///< Currently 'active' solo key, that is the key to which the currently sounding voice belongs to (only if SoloMode is enabled) + bool SustainPedal; ///< true if sustain pedal is down + bool SostenutoPedal; ///< true if sostenuto pedal is down + int SostenutoKeys[128]; + int SostenutoKeyCount; + uint32_t RoundRobinIndexes[128]; + int8_t KeyDown[128]; ///< True if the respective key is currently pressed down. Currently only used as built-in instrument script array variable %KEY_DOWN. It is currently not used by the sampler for any other purpose. + + virtual void ProcessReleaseTriggerBySustain(RTList::Iterator& itEvent) = 0; + }; + template - class MidiKeyboardManager { + class MidiKeyboardManager : public MidiKeyboardManagerBase { public: /** @brief Voice Stealing Algorithms * @@ -115,39 +164,46 @@ * * Reflects runtime informations for one MIDI key. */ - class MidiKey { + class MidiKey : public MidiKeyBase { public: - RTList* pActiveVoices; ///< Contains the active voices associated with the MIDI key. - bool KeyPressed; ///< Is true if the respective MIDI key is currently pressed. - bool Active; ///< If the key contains active voices. - bool ReleaseTrigger; ///< If we have to launch release triggered voice(s) when the key is released - Pool::Iterator itSelf; ///< hack to allow fast deallocation of the key from the list of active keys - RTList* pEvents; ///< Key specific events (only Note-on, Note-off and sustain pedal currently) - int VoiceTheftsQueued; ///< Amount of voices postponed due to shortage of voices. - uint32_t* pRoundRobinIndex; ///< For the round robin dimension: current articulation for this key, will be incremented for each note on - uint8_t Velocity; ///< Latest Note-on velocity for this key - unsigned long NoteOnTime; ///< Time for latest Note-on event for this key + RTList< Note >* pActiveNotes; ///< Contains the active notes associated with the MIDI key. MidiKey() { - pActiveVoices = NULL; + pActiveNotes = NULL; KeyPressed = false; Active = false; - ReleaseTrigger = false; + ReleaseTrigger = release_trigger_none; pEvents = NULL; VoiceTheftsQueued = 0; + Volume = 1.0f; + PanLeft = 1.0f; + PanRight = 1.0f; } void Reset() { - if (pActiveVoices) pActiveVoices->clear(); + if (pActiveNotes) { + RTListNoteIterator itNote = pActiveNotes->first(); + RTListNoteIterator itNotesEnd = pActiveNotes->end(); + for (; itNote != itNotesEnd; ++itNote) { // iterate through all active notes on this key + itNote->reset(); + } + pActiveNotes->clear(); + } if (pEvents) pEvents->clear(); KeyPressed = false; Active = false; - ReleaseTrigger = false; + ReleaseTrigger = release_trigger_none; itSelf = Pool::Iterator(); VoiceTheftsQueued = 0; + Volume = 1.0f; + PanLeft = 1.0f; + PanRight = 1.0f; + ReverbSend = optional::nothing; + ChorusSend = optional::nothing; } }; + typedef typename RTList< Note >::Iterator RTListNoteIterator; typedef typename RTList::Iterator RTListVoiceIterator; typedef typename Pool::Iterator PoolVoiceIterator; @@ -174,16 +230,8 @@ }; MidiKey* pMIDIKeyInfo; ///< Contains all active voices sorted by MIDI key number and other informations to the respective MIDI key - Pool* pActiveKeys; ///< Holds all keys in it's allocation list with active voices. - bool SoloMode; ///< in Solo Mode we only play one voice (group) at a time - int SoloKey; ///< Currently 'active' solo key, that is the key to which the currently sounding voice belongs to (only if SoloMode is enabled) - bool SustainPedal; ///< true if sustain pedal is down - bool SostenutoPedal; ///< true if sostenuto pedal is down - int SostenutoKeys[128]; - int SostenutoKeyCount; - uint32_t RoundRobinIndexes[128]; - MidiKeyboardManager() { + MidiKeyboardManager(AbstractEngineChannel* pEngineChannel) { pMIDIKeyInfo = new MidiKey[128]; pActiveKeys = new Pool(128); SoloMode = false; @@ -191,12 +239,15 @@ SostenutoPedal = false; for (int i = 0 ; i < 128 ; i++) { RoundRobinIndexes[i] = 0; + KeyDown[i] = false; // by default use one counter for each key (the // gig engine will change this to one counter per // region) pMIDIKeyInfo[i].pRoundRobinIndex = &RoundRobinIndexes[i]; } + m_engineChannel = pEngineChannel; + m_voicePool = NULL; } virtual ~MidiKeyboardManager() { @@ -209,38 +260,46 @@ SoloKey = -1; // no solo key active yet // reset key info - for (uint i = 0; i < 128; i++) pMIDIKeyInfo[i].Reset(); + for (uint i = 0; i < 128; i++) { + pMIDIKeyInfo[i].Reset(); + KeyDown[i] = false; + if (m_engineChannel->pScript) + m_engineChannel->pScript->pKeyEvents[i]->clear(); + } // free all active keys pActiveKeys->clear(); } - void AllocateActiveVoices(Pool* pVoicePool) { - DeleteActiveVoices(); + void AllocateActiveNotesLists(Pool< Note >* pNotePool, Pool* pVoicePool) { + DeleteActiveNotesLists(); + + m_voicePool = pVoicePool; for (uint i = 0; i < 128; i++) { - pMIDIKeyInfo[i].pActiveVoices = new RTList(pVoicePool); + pMIDIKeyInfo[i].pActiveNotes = new RTList< Note >(pNotePool); } } - void DeleteActiveVoices() { + void DeleteActiveNotesLists() { for (uint i = 0; i < 128; i++) { - if (pMIDIKeyInfo[i].pActiveVoices) { - delete pMIDIKeyInfo[i].pActiveVoices; - pMIDIKeyInfo[i].pActiveVoices = NULL; + if (pMIDIKeyInfo[i].pActiveNotes) { + delete pMIDIKeyInfo[i].pActiveNotes; + pMIDIKeyInfo[i].pActiveNotes = NULL; } } + m_voicePool = NULL; } - void AllocateEvents(Pool* pEventPool) { - DeleteEvents(); + void AllocateEventsLists(Pool* pEventPool) { + DeleteEventsLists(); for (uint i = 0; i < 128; i++) { pMIDIKeyInfo[i].pEvents = new RTList(pEventPool); } } - void DeleteEvents() { + void DeleteEventsLists() { for (uint i = 0; i < 128; i++) { if (pMIDIKeyInfo[i].pEvents) { delete pMIDIKeyInfo[i].pEvents; @@ -249,12 +308,27 @@ } } - void ClearAllActiveKeyEvents() { + /*void ClearAllActiveKeyEvents() { RTList::Iterator iuiKey = pActiveKeys->first(); RTList::Iterator end = pActiveKeys->end(); for(; iuiKey != end; ++iuiKey) { pMIDIKeyInfo[*iuiKey].pEvents->clear(); // free all events on the key } + }*/ + + /** + * Make sure the passed MIDI key is part of the list of active keys, + * if it is not already, then add it to that list. Accordingly it is + * safe to call this method even if the requested key is already + * marked as active. + */ + void markKeyAsActive(MidiKey* pKey) { + if (!pKey->Active) { // mark as active key + pKey->Active = true; + pKey->itSelf = pActiveKeys->allocAppend(); + const int iKey = pKey - &pMIDIKeyInfo[0]; + *pKey->itSelf = iKey; + } } /** @@ -267,7 +341,7 @@ */ void FreeVoice(PoolVoiceIterator& itVoice) { if (itVoice) { - MidiKey* pKey = &pMIDIKeyInfo[itVoice->MIDIKey]; + //MidiKey* pKey = &pMIDIKeyInfo[itVoice->MIDIKey]; // if the sample and dimension region belong to an // instrument that is unloaded, tell the disk thread to @@ -279,7 +353,7 @@ } // free the voice object - pKey->pActiveVoices->free(itVoice); + m_voicePool->free(itVoice); } else std::cerr << "Couldn't release voice! (!itVoice)\n" << std::flush; } @@ -292,11 +366,13 @@ * @param pKey - key which is now inactive */ void FreeKey(MidiKey* pKey) { - if (pKey->pActiveVoices->isEmpty()) { + if (pKey->pActiveNotes->isEmpty()) { + if (m_engineChannel->pScript) + m_engineChannel->pScript->pKeyEvents[pKey->itSelf]->clear(); pKey->Active = false; pActiveKeys->free(pKey->itSelf); // remove key from list of active keys pKey->itSelf = RTList::Iterator(); - pKey->ReleaseTrigger = false; + pKey->ReleaseTrigger = release_trigger_none; pKey->pEvents->clear(); dmsg(3,("Key has no more voices now\n")); } @@ -306,30 +382,40 @@ /** * Free all keys which have no active voices left */ - void FreeAllInactiveKyes() { + void FreeAllInactiveKeys() { RTList::Iterator iuiKey = pActiveKeys->first(); RTList::Iterator end = pActiveKeys->end(); while (iuiKey != end) { // iterate through all active keys - MidiKey* pKey = &pMIDIKeyInfo[*iuiKey]; + MidiKey* pKey = &pMIDIKeyInfo[*iuiKey]; ++iuiKey; - if (pKey->pActiveVoices->isEmpty()) FreeKey(pKey); - #if CONFIG_DEVMODE - else { // just a sanity check for debugging - RTListVoiceIterator itVoice = pKey->pActiveVoices->first(); - RTListVoiceIterator = pKey->pActiveVoices->end(); - for (; itVoice != itVoicesEnd; ++itVoice) { // iterate through all voices on this key - if (itVoice->itKillEvent) { - dmsg(1,("gig::Engine: ERROR, killed voice survived !!!\n")); + for (RTListNoteIterator itNote = pKey->pActiveNotes->first(), + itNotesEnd = pKey->pActiveNotes->end(); + itNote != itNotesEnd; ++itNote) + { // iterate over all active notes on that key ... + if (itNote->pActiveVoices->isEmpty()) { // free note ... + itNote->reset(); + pKey->pActiveNotes->free(itNote); + } + #if CONFIG_DEVMODE + else { // just a sanity check for debugging + RTListVoiceIterator itVoice = itNote->pActiveVoices->first(); + RTListVoiceIterator itVoicesEnd = itNote->pActiveVoices->end(); + for (; itVoice != itVoicesEnd; ++itVoice) { // iterate through all voices on this key + if (itVoice->itKillEvent) { + dmsg(1,("gig::Engine: ERROR, killed voice survived !!!\n")); + } } } + #endif // CONFIG_DEVMODE } - #endif // CONFIG_DEVMODE + if (pKey->pActiveNotes->isEmpty()) FreeKey(pKey); } } int StealVoice ( Pool::Iterator& itNoteOnEvent, RTListVoiceIterator* LastStolenVoice, + RTListNoteIterator* LastStolenNote, RTList::Iterator* LastStolenKey ) { RTListVoiceIterator itSelectedVoice; @@ -343,18 +429,22 @@ // 'oldestkey' algorithm case voice_steal_algo_oldestvoiceonkey: { MidiKey* pSelectedKey = &pMIDIKeyInfo[itNoteOnEvent->Param.Note.Key]; - itSelectedVoice = pSelectedKey->pActiveVoices->first(); - // proceed iterating if voice was created in this fragment cycle - while (itSelectedVoice && !itSelectedVoice->IsStealable()) ++itSelectedVoice; - // if we haven't found a voice then proceed with algorithm 'oldestkey' - if (itSelectedVoice && itSelectedVoice->IsStealable()) break; + for (RTListNoteIterator itNote = pSelectedKey->pActiveNotes->first(), + itNotesEnd = pSelectedKey->pActiveNotes->end(); + itNote != itNotesEnd; ++itNote) + { + for (itSelectedVoice = itNote->pActiveVoices->first(); itSelectedVoice; ++itSelectedVoice) + if (itSelectedVoice->IsStealable()) // proceed iterating if voice was created in this audio fragment cycle + goto voiceFound; // selection succeeded + } + // if we haven't found a voice then proceed with algorithm 'oldestkey' ... } // no break - intentional ! // try to pick the oldest voice on the oldest active key // from the same engine channel // (caution: must stay after 'oldestvoiceonkey' algorithm !) case voice_steal_algo_oldestkey: { - // if we already stole in this fragment, try to proceed on same key + // if we already stole in this fragment, try to proceed to steal on same note if (*LastStolenVoice) { itSelectedVoice = *LastStolenVoice; do { @@ -367,21 +457,44 @@ break; // selection succeeded } } + + // get (next) oldest note + if (*LastStolenNote) { + for (RTListNoteIterator itNote = ++(*LastStolenNote); + itNote; ++itNote) + { + for (itSelectedVoice = itNote->pActiveVoices->first(); itSelectedVoice; ++itSelectedVoice) { + // proceed iterating if voice was created in this audio fragment cycle + if (itSelectedVoice->IsStealable()) { + // remember which voice on which note we stole, so we can simply proceed on next voice stealing + *LastStolenNote = itNote; + *LastStolenVoice = itSelectedVoice; + goto voiceFound; // selection succeeded + } + } + } + } + // get (next) oldest key RTList::Iterator iuiSelectedKey = (*LastStolenKey) ? ++(*LastStolenKey) : pActiveKeys->first(); - while (iuiSelectedKey) { + for (; iuiSelectedKey; ++iuiSelectedKey) { MidiKey* pSelectedKey = &pMIDIKeyInfo[*iuiSelectedKey]; - itSelectedVoice = pSelectedKey->pActiveVoices->first(); - // proceed iterating if voice was created in this fragment cycle - while (itSelectedVoice && !itSelectedVoice->IsStealable()) ++itSelectedVoice; - // found a "stealable" voice ? - if (itSelectedVoice && itSelectedVoice->IsStealable()) { - // remember which voice on which key we stole, so we can simply proceed on next voice stealing - *LastStolenKey = iuiSelectedKey; - *LastStolenVoice = itSelectedVoice; - break; // selection succeeded + + for (RTListNoteIterator itNote = pSelectedKey->pActiveNotes->first(), + itNotesEnd = pSelectedKey->pActiveNotes->end(); + itNote != itNotesEnd; ++itNote) + { + for (itSelectedVoice = itNote->pActiveVoices->first(); itSelectedVoice; ++itSelectedVoice) { + // proceed iterating if voice was created in this audio fragment cycle + if (itSelectedVoice->IsStealable()) { + // remember which voice on which key we stole, so we can simply proceed on next voice stealing + *LastStolenKey = iuiSelectedKey; + *LastStolenNote = itNote; + *LastStolenVoice = itSelectedVoice; + goto voiceFound; // selection succeeded + } + } } - ++iuiSelectedKey; // get next oldest key } break; } @@ -393,6 +506,8 @@ return -1; } } + + voiceFound: if (!itSelectedVoice || !itSelectedVoice->IsStealable()) return -1; @@ -425,11 +540,12 @@ RTList::Iterator itNewEvent = pKey->pEvents->allocAppend(); if (itNewEvent) { *itNewEvent = *itReleaseEvent; // copy original event (to the key's event list) - itNewEvent->Type = Event::type_release; // transform event type + itNewEvent->Type = Event::type_release_key; // transform event type } else dmsg(1,("Event pool emtpy!\n")); } } + /** * Kill all active voices. * @returns The number of voices. @@ -437,15 +553,21 @@ int KillAllVoices(Pool::Iterator& itKillEvent) { int count = 0; - RTList::Iterator iuiKey = pActiveKeys->first(); + RTList::Iterator iuiKey = pActiveKeys->first(); RTList::Iterator end = pActiveKeys->end(); for (; iuiKey != end; ++iuiKey) { // iterate through all active keys MidiKey* pKey = &pMIDIKeyInfo[*iuiKey]; - RTListVoiceIterator itVoice = pKey->pActiveVoices->first(); - RTListVoiceIterator itVoicesEnd = pKey->pActiveVoices->end(); - for (; itVoice != itVoicesEnd; ++itVoice) { // iterate through all voices on this key - itVoice->Kill(itKillEvent); - count++; + + for (RTListNoteIterator itNote = pKey->pActiveNotes->first(), + itNotesEnd = pKey->pActiveNotes->end(); + itNote != itNotesEnd; ++itNote) + { + RTListVoiceIterator itVoice = itNote->pActiveVoices->first(); + RTListVoiceIterator itVoicesEnd = itNote->pActiveVoices->end(); + for (; itVoice != itVoicesEnd; ++itVoice) { // iterate through all voices on this key + itVoice->Kill(itKillEvent); + count++; + } } } @@ -459,20 +581,27 @@ int KillAllVoicesImmediately() { int iPendingStreamDeletions = 0; - RTList::Iterator iuiKey = pActiveKeys->first(); + RTList::Iterator iuiKey = pActiveKeys->first(); RTList::Iterator end = pActiveKeys->end(); for (; iuiKey != end; ++iuiKey) { // iterate through all active keys MidiKey* pKey = &pMIDIKeyInfo[*iuiKey]; - RTListVoiceIterator itVoice = pKey->pActiveVoices->first(); - RTListVoiceIterator itVoicesEnd = pKey->pActiveVoices->end(); - for (; itVoice != itVoicesEnd; ++itVoice) { // iterate through all voices on this key - // request a notification from disk thread side for stream deletion - const Stream::Handle hStream = itVoice->KillImmediately(true); - if (hStream != Stream::INVALID_HANDLE) { // voice actually used a stream - iPendingStreamDeletions++; + + for (RTListNoteIterator itNote = pKey->pActiveNotes->first(), + itNotesEnd = pKey->pActiveNotes->end(); + itNote != itNotesEnd; ++itNote) + { + RTListVoiceIterator itVoice = itNote->pActiveVoices->first(); + RTListVoiceIterator itVoicesEnd = itNote->pActiveVoices->end(); + for (; itVoice != itVoicesEnd; ++itVoice) { // iterate through all voices on this key + // request a notification from disk thread side for stream deletion + const Stream::Handle hStream = itVoice->KillImmediately(true); + if (hStream != Stream::INVALID_HANDLE) { // voice actually used a stream + iPendingStreamDeletions++; + } + // free the voice to the voice pool and update key info + itVoice->VoiceFreed(); + FreeVoice(itVoice); } - // free the voice to the voice pool and update key info - FreeVoice(itVoice); } } @@ -484,14 +613,20 @@ * samples they use should be released to the instrument manager when the voices die. */ void MarkAllActiveVoicesAsOrphans() { - RTList::Iterator iuiKey = pActiveKeys->first(); + RTList::Iterator iuiKey = pActiveKeys->first(); RTList::Iterator end = pActiveKeys->end(); for (; iuiKey != end; ++iuiKey) { // iterate through all active keys MidiKey* pKey = &pMIDIKeyInfo[*iuiKey]; - RTListVoiceIterator itVoice = pKey->pActiveVoices->first(); - RTListVoiceIterator itVoicesEnd = pKey->pActiveVoices->end(); - for (; itVoice != itVoicesEnd; ++itVoice) { // iterate through all voices on this key - itVoice->Orphan = true; + + for (RTListNoteIterator itNote = pKey->pActiveNotes->first(), + itNotesEnd = pKey->pActiveNotes->end(); + itNote != itNotesEnd; ++itNote) + { + RTListVoiceIterator itVoice = itNote->pActiveVoices->first(); + RTListVoiceIterator itVoicesEnd = itNote->pActiveVoices->end(); + for (; itVoice != itVoicesEnd; ++itVoice) { // iterate through all voices on this key + itVoice->Orphan = true; + } } } } @@ -499,20 +634,45 @@ void ProcessActiveVoices(VoiceHandler* pVoiceHandler) { if (pVoiceHandler == NULL) return; - RTList::Iterator iuiKey = pActiveKeys->first(); + RTList::Iterator iuiKey = pActiveKeys->first(); RTList::Iterator end = pActiveKeys->end(); for (; iuiKey != end; ++iuiKey) { // iterate through all active keys MidiKey* pKey = &pMIDIKeyInfo[*iuiKey]; if (!pVoiceHandler->Process(pKey)) continue; - RTListVoiceIterator itVoice = pKey->pActiveVoices->first(); - RTListVoiceIterator itVoicesEnd = pKey->pActiveVoices->end(); - for (; itVoice != itVoicesEnd; ++itVoice) { // iterate through all voices on this key - pVoiceHandler->Process(itVoice); + for (RTListNoteIterator itNote = pKey->pActiveNotes->first(), + itNotesEnd = pKey->pActiveNotes->end(); + itNote != itNotesEnd; ++itNote) + { + RTListVoiceIterator itVoice = itNote->pActiveVoices->first(); + RTListVoiceIterator itVoicesEnd = itNote->pActiveVoices->end(); + for (; itVoice != itVoicesEnd; ++itVoice) { // iterate through all voices on this key + pVoiceHandler->Process(itVoice); + } } } } + + /** + * Recalculate the pitch of all active voices. + */ + void OnScaleTuningChanged() { + RTList::Iterator iuiKey = pActiveKeys->first(); + for (; iuiKey; ++iuiKey) { + MidiKey* pKey = &pMIDIKeyInfo[*iuiKey]; + for (RTListNoteIterator itNote = pKey->pActiveNotes->first(), + itNotesEnd = pKey->pActiveNotes->end(); + itNote != itNotesEnd; ++itNote) + { + RTListVoiceIterator itVoice = itNote->pActiveVoices->first(); + for (; itVoice; ++itVoice) { + itVoice->onScaleTuningChanged(); + } + } + } + } + void ProcessSustainPedalDown(Pool::Iterator& itEvent) { // Cancel release process of all voices RTList::Iterator iuiKey = pActiveKeys->first(); @@ -522,7 +682,7 @@ RTList::Iterator itNewEvent = pKey->pEvents->allocAppend(); if (itNewEvent) { *itNewEvent = *itEvent; // copy event to the key's own event list - itNewEvent->Type = Event::type_cancel_release; // transform event type + itNewEvent->Type = Event::type_cancel_release_key; // transform event type } else dmsg(1,("Event pool emtpy!\n")); } @@ -538,7 +698,26 @@ RTList::Iterator itNewEvent = pKey->pEvents->allocAppend(); if (itNewEvent) { *itNewEvent = *itEvent; // copy event to the key's own event list - itNewEvent->Type = Event::type_release; // transform event type + itNewEvent->Type = Event::type_release_key; // transform event type + itNewEvent->Param.Note.Key = *iuiKey; + itNewEvent->Param.Note.Velocity = 127; + + // process release trigger (if requested) + if (pKey->ReleaseTrigger & release_trigger_sustain) { + if (pKey->ReleaseTrigger & release_trigger_sustain_keyvelocity) + itNewEvent->Param.Note.Velocity = pKey->Velocity; + + //HACK: set sustain CC (64) as "pressed down" for a short moment, so that release trigger voices can distinguish between note off and sustain pedal up cases + AbstractEngineChannel* pChannel = (AbstractEngineChannel*) itEvent->pEngineChannel; + const int8_t CC64Value = pChannel->ControllerTable[64]; + pChannel->ControllerTable[64] = 127; + + // now spawn release trigger voices (if required) + ProcessReleaseTriggerBySustain(itNewEvent); + + //HACK: reset sustain pedal CC value to old one (see comment above) + pChannel->ControllerTable[64] = CC64Value; + } } else dmsg(1,("Event pool emtpy!\n")); } @@ -546,6 +725,19 @@ } /** + * Whether @a key is still kept active due to sostenuto pedal usage. + * + * @param key - note number of key + */ + inline bool SostenutoActiveOnKey(int key) const { + if (SostenutoPedal) { + for (int i = 0; i < SostenutoKeyCount; i++) + if (key == SostenutoKeys[i]) return true; + } + return false; + } + + /** * Determines whether the specified voice should be released. * * @param pEngineChannel - The engine channel on which the voice should be checked @@ -554,12 +746,7 @@ */ bool ShouldReleaseVoice(int Key) { if (SustainPedal) return false; - - if (SostenutoPedal) { - for (int i = 0; i < SostenutoKeyCount; i++) - if (Key == SostenutoKeys[i]) return false; - } - + if (SostenutoActiveOnKey(Key)) return false; return true; } @@ -581,7 +768,7 @@ RTList::Iterator itNewEvent = pKey->pEvents->allocAppend(); if (itNewEvent) { *itNewEvent = *itEvent; // copy event to the key's own event list - itNewEvent->Type = Event::type_release; // transform event type + itNewEvent->Type = Event::type_release_key; // transform event type } else dmsg(1,("Event pool emtpy!\n")); } @@ -593,6 +780,9 @@ void RemoveMidiKeyboardListener(MidiKeyboardListener* l) { listeners.RemoveListener(l); } protected: + AbstractEngineChannel* m_engineChannel; + Pool* m_voicePool; + class Listeners : public MidiKeyboardListener, public ListenerList { public: REGISTER_FIRE_EVENT_METHOD_ARG2(PreProcessNoteOn, uint8_t, uint8_t)