33 |
#include "InstrumentManager.h" |
#include "InstrumentManager.h" |
34 |
#include "../common/global_private.h" |
#include "../common/global_private.h" |
35 |
|
|
36 |
|
// a bit headroom over CONFIG_MAX_VOICES to avoid minor complications i.e. under voice stealing conditions |
37 |
|
#define MAX_NOTES_HEADROOM 3 |
38 |
|
#define GLOBAL_MAX_NOTES (GLOBAL_MAX_VOICES * MAX_NOTES_HEADROOM) |
39 |
|
|
40 |
namespace LinuxSampler { |
namespace LinuxSampler { |
41 |
|
|
49 |
class IM /* Instrument Manager */, |
class IM /* Instrument Manager */, |
50 |
class I /* Instrument */ |
class I /* Instrument */ |
51 |
> |
> |
52 |
class EngineBase: public AbstractEngine, public RegionPools<R>, public VoicePool<V> { |
class EngineBase: public AbstractEngine, public RegionPools<R>, public NotePool<V> { |
53 |
|
|
54 |
public: |
public: |
55 |
|
typedef typename RTList< Note<V> >::Iterator NoteIterator; |
56 |
typedef typename RTList<V>::Iterator VoiceIterator; |
typedef typename RTList<V>::Iterator VoiceIterator; |
57 |
typedef typename Pool<V>::Iterator PoolVoiceIterator; |
typedef typename Pool<V>::Iterator PoolVoiceIterator; |
58 |
typedef typename RTList<RR*>::Iterator RootRegionIterator; |
typedef typename RTList<RR*>::Iterator RootRegionIterator; |
59 |
typedef typename MidiKeyboardManager<V>::MidiKey MidiKey; |
typedef typename MidiKeyboardManager<V>::MidiKey MidiKey; |
60 |
|
|
61 |
EngineBase() : SuspendedRegions(128) { |
EngineBase() : SuspendedRegions(128), noteIDPool(GLOBAL_MAX_NOTES) { |
62 |
pDiskThread = NULL; |
pDiskThread = NULL; |
63 |
|
pNotePool = new Pool< Note<V> >(GLOBAL_MAX_NOTES); |
64 |
|
pNotePool->setPoolElementIDsReservedBits(INSTR_SCRIPT_EVENT_ID_RESERVED_BITS); |
65 |
pVoicePool = new Pool<V>(GLOBAL_MAX_VOICES); |
pVoicePool = new Pool<V>(GLOBAL_MAX_VOICES); |
66 |
pRegionPool[0] = new Pool<R*>(GLOBAL_MAX_VOICES); |
pRegionPool[0] = new Pool<R*>(GLOBAL_MAX_VOICES); |
67 |
pRegionPool[1] = new Pool<R*>(GLOBAL_MAX_VOICES); |
pRegionPool[1] = new Pool<R*>(GLOBAL_MAX_VOICES); |
68 |
pVoiceStealingQueue = new RTList<Event>(pEventPool); |
pVoiceStealingQueue = new RTList<Event>(pEventPool); |
69 |
iMaxDiskStreams = GLOBAL_MAX_STREAMS; |
iMaxDiskStreams = GLOBAL_MAX_STREAMS; |
70 |
|
|
71 |
for (VoiceIterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) { |
// init all Voice objects in voice pool |
72 |
|
for (VoiceIterator iterVoice = pVoicePool->allocAppend(); |
73 |
|
iterVoice; iterVoice = pVoicePool->allocAppend()) |
74 |
|
{ |
75 |
iterVoice->SetEngine(this); |
iterVoice->SetEngine(this); |
76 |
} |
} |
77 |
pVoicePool->clear(); |
pVoicePool->clear(); |
78 |
|
|
79 |
|
// init all Note objects in note pool |
80 |
|
for (NoteIterator itNote = pNotePool->allocAppend(); itNote; |
81 |
|
itNote = pNotePool->allocAppend()) |
82 |
|
{ |
83 |
|
itNote->init(pVoicePool, ¬eIDPool); |
84 |
|
} |
85 |
|
pNotePool->clear(); |
86 |
|
|
87 |
ResetInternal(); |
ResetInternal(); |
88 |
ResetScaleTuning(); |
ResetScaleTuning(); |
89 |
ResetSuspendedRegions(); |
ResetSuspendedRegions(); |
97 |
dmsg(1,("OK\n")); |
dmsg(1,("OK\n")); |
98 |
} |
} |
99 |
|
|
100 |
|
if (pNotePool) { |
101 |
|
pNotePool->clear(); |
102 |
|
delete pNotePool; |
103 |
|
} |
104 |
|
|
105 |
if (pVoicePool) { |
if (pVoicePool) { |
106 |
pVoicePool->clear(); |
pVoicePool->clear(); |
107 |
delete pVoicePool; |
delete pVoicePool; |
258 |
pChannel->ResetRegionsInUse(pRegionPool); |
pChannel->ResetRegionsInUse(pRegionPool); |
259 |
} |
} |
260 |
|
|
261 |
|
// FIXME: Shouldn't all those pool elements be freed before resizing the pools? |
262 |
try { |
try { |
263 |
pVoicePool->resizePool(iVoices); |
pVoicePool->resizePool(iVoices); |
264 |
|
pNotePool->resizePool(iVoices * MAX_NOTES_HEADROOM); |
265 |
|
noteIDPool.resizePool(iVoices * MAX_NOTES_HEADROOM); |
266 |
} catch (...) { |
} catch (...) { |
267 |
throw Exception("FATAL: Could not resize voice pool!"); |
throw Exception("FATAL: Could not resize voice pool!"); |
268 |
} |
} |
269 |
|
|
270 |
for (VoiceIterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) { |
for (VoiceIterator iterVoice = pVoicePool->allocAppend(); |
271 |
|
iterVoice; iterVoice = pVoicePool->allocAppend()) |
272 |
|
{ |
273 |
iterVoice->SetEngine(this); |
iterVoice->SetEngine(this); |
274 |
iterVoice->pDiskThread = this->pDiskThread; |
iterVoice->pDiskThread = this->pDiskThread; |
275 |
} |
} |
276 |
pVoicePool->clear(); |
pVoicePool->clear(); |
277 |
|
|
278 |
|
for (NoteIterator itNote = pNotePool->allocAppend(); itNote; |
279 |
|
itNote = pNotePool->allocAppend()) |
280 |
|
{ |
281 |
|
itNote->init(pVoicePool, ¬eIDPool); |
282 |
|
} |
283 |
|
pNotePool->clear(); |
284 |
|
|
285 |
PostSetMaxVoices(iVoices); |
PostSetMaxVoices(iVoices); |
286 |
ResumeAll(); |
ResumeAll(); |
287 |
} |
} |
350 |
MinFadeOutSamples = MaxSamplesPerCycle; |
MinFadeOutSamples = MaxSamplesPerCycle; |
351 |
// lower minimum release time |
// lower minimum release time |
352 |
const float minReleaseTime = (float) MaxSamplesPerCycle / (float) SampleRate; |
const float minReleaseTime = (float) MaxSamplesPerCycle / (float) SampleRate; |
353 |
|
pVoicePool->clear(); |
354 |
for (VoiceIterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) { |
for (VoiceIterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) { |
355 |
iterVoice->CalculateFadeOutCoeff(minReleaseTime, SampleRate); |
iterVoice->CalculateFadeOutCoeff(minReleaseTime, SampleRate); |
356 |
} |
} |
371 |
exit(EXIT_FAILURE); |
exit(EXIT_FAILURE); |
372 |
} |
} |
373 |
|
|
374 |
|
pVoicePool->clear(); |
375 |
for (VoiceIterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) { |
for (VoiceIterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) { |
376 |
iterVoice->pDiskThread = this->pDiskThread; |
iterVoice->pDiskThread = this->pDiskThread; |
377 |
dmsg(3,("d")); |
dmsg(3,("d")); |
601 |
return pRegionPool[index]; |
return pRegionPool[index]; |
602 |
} |
} |
603 |
|
|
604 |
// implementation of abstract method derived from class 'LinuxSampler::VoicePool' |
// implementation of abstract methods derived from class 'LinuxSampler::NotePool' |
605 |
virtual Pool<V>* GetVoicePool() { return pVoicePool; } |
virtual Pool<V>* GetVoicePool() OVERRIDE { return pVoicePool; } |
606 |
|
virtual Pool< Note<V> >* GetNotePool() OVERRIDE { return pNotePool; } |
607 |
|
virtual Pool<note_id_t>* GetNodeIDPool() OVERRIDE { return ¬eIDPool; } |
608 |
|
|
609 |
D* GetDiskThread() { return pDiskThread; } |
D* GetDiskThread() { return pDiskThread; } |
610 |
|
|
624 |
} |
} |
625 |
|
|
626 |
virtual bool Process(MidiKey* pMidiKey) OVERRIDE { |
virtual bool Process(MidiKey* pMidiKey) OVERRIDE { |
627 |
VoiceIterator itVoice = pMidiKey->pActiveVoices->first(); |
NoteIterator itNote = pMidiKey->pActiveNotes->first(); |
628 |
|
VoiceIterator itVoice = itNote->pActiveVoices->first(); |
629 |
// if current key is not associated with this region, skip this key |
// if current key is not associated with this region, skip this key |
630 |
if (itVoice->GetRegion()->GetParent() != pPendingRegionSuspension) return false; |
if (itVoice->GetRegion()->GetParent() != pPendingRegionSuspension) return false; |
631 |
|
|
648 |
|
|
649 |
int ActiveVoiceCountTemp; ///< number of currently active voices (for internal usage, will be used for incrementation) |
int ActiveVoiceCountTemp; ///< number of currently active voices (for internal usage, will be used for incrementation) |
650 |
VoiceIterator itLastStolenVoice; ///< Only for voice stealing: points to the last voice which was theft in current audio fragment, NULL otherwise. |
VoiceIterator itLastStolenVoice; ///< Only for voice stealing: points to the last voice which was theft in current audio fragment, NULL otherwise. |
651 |
|
NoteIterator itLastStolenNote; ///< Only for voice stealing: points to the last note from which was theft in current audio fragment, NULL otherwise. |
652 |
RTList<uint>::Iterator iuiLastStolenKey; ///< Only for voice stealing: key number of last key on which the last voice was theft in current audio fragment, NULL otherwise. |
RTList<uint>::Iterator iuiLastStolenKey; ///< Only for voice stealing: key number of last key on which the last voice was theft in current audio fragment, NULL otherwise. |
653 |
EngineChannelBase<V, R, I>* pLastStolenChannel; ///< Only for voice stealing: points to the engine channel on which the previous voice was stolen in this audio fragment. |
EngineChannelBase<V, R, I>* pLastStolenChannel; ///< Only for voice stealing: points to the engine channel on which the previous voice was stolen in this audio fragment. |
654 |
VoiceIterator itLastStolenVoiceGlobally; ///< Same as itLastStolenVoice, but engine globally |
VoiceIterator itLastStolenVoiceGlobally; ///< Same as itLastStolenVoice, but engine globally |
655 |
|
NoteIterator itLastStolenNoteGlobally; ///< Same as itLastStolenNote, but engine globally |
656 |
RTList<uint>::Iterator iuiLastStolenKeyGlobally; ///< Same as iuiLastStolenKey, but engine globally |
RTList<uint>::Iterator iuiLastStolenKeyGlobally; ///< Same as iuiLastStolenKey, but engine globally |
657 |
RTList<Event>* pVoiceStealingQueue; ///< All voice-launching events which had to be postponed due to free voice shortage. |
RTList<Event>* pVoiceStealingQueue; ///< All voice-launching events which had to be postponed due to free voice shortage. |
658 |
Mutex ResetInternalMutex; ///< Mutex to protect the ResetInternal function for concurrent usage (e.g. by the lscp and instrument loader threads). |
Mutex ResetInternalMutex; ///< Mutex to protect the ResetInternal function for concurrent usage (e.g. by the lscp and instrument loader threads). |
659 |
int iMaxDiskStreams; |
int iMaxDiskStreams; |
660 |
|
|
661 |
|
NoteBase* NoteByID(note_id_t id) OVERRIDE { |
662 |
|
NoteIterator itNote = GetNotePool()->fromID(id); |
663 |
|
if (!itNote) return NULL; |
664 |
|
return &*itNote; |
665 |
|
} |
666 |
|
|
667 |
|
/** |
668 |
|
* Gets a new @c Note object from the note pool, initializes it |
669 |
|
* appropriately, links it with requested parent note (if |
670 |
|
* requested), moves it to the appropriate key's list of active |
671 |
|
* notes it, and sticks the new note's unique ID to the |
672 |
|
* passed @a pNoteOnEvent. |
673 |
|
* |
674 |
|
* @param pEngineChannel - engine channel on which this event happened |
675 |
|
* @param pNoteOnEvent - event which caused this |
676 |
|
* @returns new note's unique ID (or zero on error) |
677 |
|
*/ |
678 |
|
note_id_t LaunchNewNote(LinuxSampler::EngineChannel* pEngineChannel, Event* pNoteOnEvent) OVERRIDE { |
679 |
|
EngineChannelBase<V, R, I>* pChannel = static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel); |
680 |
|
Pool< Note<V> >* pNotePool = GetNotePool(); |
681 |
|
|
682 |
|
if (pNotePool->poolIsEmpty()) { |
683 |
|
dmsg(1,("Engine: Could not launch new note; Note pool empty!\n")); |
684 |
|
return 0; // error |
685 |
|
} |
686 |
|
|
687 |
|
// create a new note (for new voices to be assigned to) |
688 |
|
//NoteIterator itNewNote = pKey->pActiveNotes->allocAppend(); |
689 |
|
NoteIterator itNewNote = pNotePool->allocAppend(); |
690 |
|
const note_id_t newNoteID = pNotePool->getID(itNewNote); |
691 |
|
|
692 |
|
// usually the new note (and its subsequent voices) will be |
693 |
|
// allocated on the key provided by the event's note number, |
694 |
|
// however if this new note is requested not to be a regular |
695 |
|
// note, but rather a child note, then this new note will be |
696 |
|
// allocated on the parent note's key instead in order to |
697 |
|
// release the child note simultaniously with its parent note |
698 |
|
itNewNote->hostKey = pNoteOnEvent->Param.Note.Key; |
699 |
|
|
700 |
|
// in case this new note was requested to be a child note, |
701 |
|
// then retrieve its parent note and link them with each other |
702 |
|
const note_id_t parentNoteID = pNoteOnEvent->Param.Note.ParentNoteID; |
703 |
|
if (parentNoteID) { |
704 |
|
NoteIterator itParentNote = pNotePool->fromID(parentNoteID); |
705 |
|
if (itParentNote) { |
706 |
|
RTList<note_id_t>::Iterator itChildNoteID = itParentNote->pChildNotes->allocAppend(); |
707 |
|
if (itChildNoteID) { |
708 |
|
// link parent and child note with each other |
709 |
|
*itChildNoteID = newNoteID; |
710 |
|
itNewNote->parentNoteID = parentNoteID; |
711 |
|
itNewNote->hostKey = itParentNote->hostKey; |
712 |
|
} else { |
713 |
|
dmsg(1,("Engine: Could not assign new note as child note; Note ID pool empty!\n")); |
714 |
|
pNotePool->free(itNewNote); |
715 |
|
return 0; // error |
716 |
|
} |
717 |
|
} else { |
718 |
|
// the parent note was apparently released already, so |
719 |
|
// free the new note again and inform caller that it |
720 |
|
// should drop the event |
721 |
|
dmsg(3,("Engine: Could not assign new note as child note; Parent note is gone!\n")); |
722 |
|
pNotePool->free(itNewNote); |
723 |
|
return 0; // error |
724 |
|
} |
725 |
|
} |
726 |
|
|
727 |
|
dmsg(2,("Launched new note on host key %d\n", itNewNote->hostKey)); |
728 |
|
|
729 |
|
// copy event which caused this note |
730 |
|
itNewNote->cause = *pNoteOnEvent; |
731 |
|
itNewNote->eventID = pEventPool->getID(pNoteOnEvent); |
732 |
|
|
733 |
|
// move new note to its host key |
734 |
|
MidiKey* pKey = &pChannel->pMIDIKeyInfo[itNewNote->hostKey]; |
735 |
|
itNewNote.moveToEndOf(pKey->pActiveNotes); |
736 |
|
|
737 |
|
// assign unique note ID of this new note to the original note on event |
738 |
|
pNoteOnEvent->Param.Note.ID = newNoteID; |
739 |
|
|
740 |
|
return newNoteID; // success |
741 |
|
} |
742 |
|
|
743 |
/** |
/** |
744 |
* Dispatch and handle all events in this audio fragment for the given |
* Dispatch and handle all events in this audio fragment for the given |
745 |
* engine channel. |
* engine channel. |
873 |
// reset voice stealing for the next engine channel (or next audio fragment) |
// reset voice stealing for the next engine channel (or next audio fragment) |
874 |
itLastStolenVoice = VoiceIterator(); |
itLastStolenVoice = VoiceIterator(); |
875 |
itLastStolenVoiceGlobally = VoiceIterator(); |
itLastStolenVoiceGlobally = VoiceIterator(); |
876 |
|
itLastStolenNote = NoteIterator(); |
877 |
|
itLastStolenNoteGlobally = NoteIterator(); |
878 |
iuiLastStolenKey = RTList<uint>::Iterator(); |
iuiLastStolenKey = RTList<uint>::Iterator(); |
879 |
iuiLastStolenKeyGlobally = RTList<uint>::Iterator(); |
iuiLastStolenKeyGlobally = RTList<uint>::Iterator(); |
880 |
pLastStolenChannel = NULL; |
pLastStolenChannel = NULL; |
966 |
|
|
967 |
// initialize/reset other members |
// initialize/reset other members |
968 |
itScriptEvent->cause = *itEvent; |
itScriptEvent->cause = *itEvent; |
|
itScriptEvent->id = pEventPool->getID(itEvent); |
|
969 |
itScriptEvent->currentHandler = 0; |
itScriptEvent->currentHandler = 0; |
970 |
itScriptEvent->executionSlices = 0; |
itScriptEvent->executionSlices = 0; |
971 |
|
// this is the native representation of the $EVENT_ID script variable |
972 |
|
itScriptEvent->id = |
973 |
|
(itEvent->Type == Event::type_note_on) |
974 |
|
? ScriptID::fromNoteID( itEvent->Param.Note.ID ) |
975 |
|
: ScriptID::fromEventID( pEventPool->getID(itEvent) ); |
976 |
|
|
977 |
// run script handler(s) |
// run script handler(s) |
978 |
VMExecStatus_t res = pScriptVM->exec( |
VMExecStatus_t res = pScriptVM->exec( |
1015 |
/** @brief Resume execution of instrument script. |
/** @brief Resume execution of instrument script. |
1016 |
* |
* |
1017 |
* Will be called to resume execution of a real-time instrument |
* Will be called to resume execution of a real-time instrument |
1018 |
* script event which has been suspended in a previous audio |
* script event which has been suspended previously. |
|
* fragment cycle. |
|
1019 |
* |
* |
1020 |
* Script execution might be suspended for various reasons. Usually |
* Script execution might be suspended for various reasons. Usually |
1021 |
* a script will be suspended if the script called the built-in |
* a script will be suspended if the script called the built-in |
1079 |
* @param itNoteOnEvent - key, velocity and time stamp of the event |
* @param itNoteOnEvent - key, velocity and time stamp of the event |
1080 |
* @returns 0 on success, a value < 0 if no active voice could be picked for voice stealing |
* @returns 0 on success, a value < 0 if no active voice could be picked for voice stealing |
1081 |
*/ |
*/ |
1082 |
int StealVoice(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNoteOnEvent) { |
int StealVoice(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNoteOnEvent) { |
1083 |
if (VoiceSpawnsLeft <= 0) { |
if (VoiceSpawnsLeft <= 0) { |
1084 |
dmsg(1,("Max. voice thefts per audio fragment reached (you may raise CONFIG_MAX_VOICES).\n")); |
dmsg(1,("Max. voice thefts per audio fragment reached (you may raise CONFIG_MAX_VOICES).\n")); |
1085 |
return -1; |
return -1; |
1087 |
|
|
1088 |
EngineChannelBase<V, R, I>* pEngineChn = static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel); |
EngineChannelBase<V, R, I>* pEngineChn = static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel); |
1089 |
|
|
1090 |
if (!pEventPool->poolIsEmpty()) { |
if (pEventPool->poolIsEmpty()) { |
1091 |
|
dmsg(1,("Event pool emtpy!\n")); |
1092 |
|
return -1; |
1093 |
|
} |
1094 |
|
|
1095 |
if(!pEngineChn->StealVoice(itNoteOnEvent, &itLastStolenVoice, &iuiLastStolenKey)) { |
if (!pEngineChn->StealVoice(itNoteOnEvent, &itLastStolenVoice, &itLastStolenNote, &iuiLastStolenKey)) { |
1096 |
--VoiceSpawnsLeft; |
--VoiceSpawnsLeft; |
1097 |
return 0; |
return 0; |
1098 |
} |
} |
1099 |
|
|
1100 |
// if we couldn't steal a voice from the same engine channel then |
// if we couldn't steal a voice from the same engine channel then |
1101 |
// steal oldest voice on the oldest key from any other engine channel |
// steal oldest voice on the oldest key from any other engine channel |
1102 |
// (the smaller engine channel number, the higher priority) |
// (the smaller engine channel number, the higher priority) |
1103 |
EngineChannelBase<V, R, I>* pSelectedChannel; |
EngineChannelBase<V, R, I>* pSelectedChannel; |
1104 |
int iChannelIndex; |
int iChannelIndex; |
1105 |
VoiceIterator itSelectedVoice; |
VoiceIterator itSelectedVoice; |
1106 |
|
|
1107 |
// select engine channel |
// select engine channel |
1108 |
if (pLastStolenChannel) { |
if (pLastStolenChannel) { |
1109 |
pSelectedChannel = pLastStolenChannel; |
pSelectedChannel = pLastStolenChannel; |
1110 |
iChannelIndex = pSelectedChannel->iEngineIndexSelf; |
iChannelIndex = pSelectedChannel->iEngineIndexSelf; |
1111 |
} else { // pick the engine channel followed by this engine channel |
} else { // pick the engine channel followed by this engine channel |
1112 |
iChannelIndex = (pEngineChn->iEngineIndexSelf + 1) % engineChannels.size(); |
iChannelIndex = (pEngineChn->iEngineIndexSelf + 1) % engineChannels.size(); |
1113 |
pSelectedChannel = static_cast<EngineChannelBase<V, R, I>*>(engineChannels[iChannelIndex]); |
pSelectedChannel = static_cast<EngineChannelBase<V, R, I>*>(engineChannels[iChannelIndex]); |
1114 |
} |
} |
1115 |
|
|
1116 |
// if we already stole in this fragment, try to proceed on same key |
// if we already stole in this fragment, try to proceed on same note |
1117 |
if (this->itLastStolenVoiceGlobally) { |
if (this->itLastStolenVoiceGlobally) { |
1118 |
itSelectedVoice = this->itLastStolenVoiceGlobally; |
itSelectedVoice = this->itLastStolenVoiceGlobally; |
1119 |
do { |
do { |
1120 |
++itSelectedVoice; |
++itSelectedVoice; |
1121 |
} while (itSelectedVoice && !itSelectedVoice->IsStealable()); // proceed iterating if voice was created in this fragment cycle |
} while (itSelectedVoice && !itSelectedVoice->IsStealable()); // proceed iterating if voice was created in this fragment cycle |
1122 |
|
} |
1123 |
|
// did we find a 'stealable' voice? |
1124 |
|
if (itSelectedVoice && itSelectedVoice->IsStealable()) { |
1125 |
|
// remember which voice we stole, so we can simply proceed on next voice stealing |
1126 |
|
this->itLastStolenVoiceGlobally = itSelectedVoice; |
1127 |
|
// done |
1128 |
|
goto stealable_voice_found; |
1129 |
|
} |
1130 |
|
|
1131 |
|
// get (next) oldest note |
1132 |
|
if (this->itLastStolenNoteGlobally) { |
1133 |
|
for (NoteIterator itNote = ++this->itLastStolenNoteGlobally; |
1134 |
|
itNote; ++itNote) |
1135 |
|
{ |
1136 |
|
for (itSelectedVoice = itNote->pActiveVoices->first(); itSelectedVoice; ++itSelectedVoice) { |
1137 |
|
// proceed iterating if voice was created in this audio fragment cycle |
1138 |
|
if (itSelectedVoice->IsStealable()) { |
1139 |
|
// remember which voice of which note we stole, so we can simply proceed on next voice stealing |
1140 |
|
this->itLastStolenNoteGlobally = itNote; |
1141 |
|
this->itLastStolenVoiceGlobally = itSelectedVoice; |
1142 |
|
goto stealable_voice_found; // selection succeeded |
1143 |
|
} |
1144 |
|
} |
1145 |
} |
} |
1146 |
|
} |
1147 |
|
|
1148 |
#if CONFIG_DEVMODE |
#if CONFIG_DEVMODE |
1149 |
EngineChannel* pBegin = pSelectedChannel; // to detect endless loop |
EngineChannel* pBegin = pSelectedChannel; // to detect endless loop |
1150 |
#endif // CONFIG_DEVMODE |
#endif // CONFIG_DEVMODE |
1151 |
|
|
1152 |
// did we find a 'stealable' voice? |
while (true) { // iterate through engine channels |
1153 |
if (itSelectedVoice && itSelectedVoice->IsStealable()) { |
// get (next) oldest key |
1154 |
// remember which voice we stole, so we can simply proceed on next voice stealing |
RTList<uint>::Iterator iuiSelectedKey = (this->iuiLastStolenKeyGlobally) ? ++this->iuiLastStolenKeyGlobally : pSelectedChannel->pActiveKeys->first(); |
1155 |
this->itLastStolenVoiceGlobally = itSelectedVoice; |
this->iuiLastStolenKeyGlobally = RTList<uint>::Iterator(); // to prevent endless loop (see line above) |
1156 |
} else while (true) { // iterate through engine channels |
while (iuiSelectedKey) { |
1157 |
// get (next) oldest key |
MidiKey* pSelectedKey = &pSelectedChannel->pMIDIKeyInfo[*iuiSelectedKey]; |
1158 |
RTList<uint>::Iterator iuiSelectedKey = (this->iuiLastStolenKeyGlobally) ? ++this->iuiLastStolenKeyGlobally : pSelectedChannel->pActiveKeys->first(); |
|
1159 |
this->iuiLastStolenKeyGlobally = RTList<uint>::Iterator(); // to prevent endless loop (see line above) |
for (NoteIterator itNote = pSelectedKey->pActiveNotes->first(), |
1160 |
while (iuiSelectedKey) { |
itNotesEnd = pSelectedKey->pActiveNotes->end(); |
1161 |
MidiKey* pSelectedKey = &pSelectedChannel->pMIDIKeyInfo[*iuiSelectedKey]; |
itNote != itNotesEnd; ++itNote) |
1162 |
itSelectedVoice = pSelectedKey->pActiveVoices->first(); |
{ |
1163 |
|
itSelectedVoice = itNote->pActiveVoices->first(); |
1164 |
// proceed iterating if voice was created in this fragment cycle |
// proceed iterating if voice was created in this fragment cycle |
1165 |
while (itSelectedVoice && !itSelectedVoice->IsStealable()) ++itSelectedVoice; |
while (itSelectedVoice && !itSelectedVoice->IsStealable()) ++itSelectedVoice; |
1166 |
// found a "stealable" voice ? |
// found a "stealable" voice ? |
1167 |
if (itSelectedVoice && itSelectedVoice->IsStealable()) { |
if (itSelectedVoice && itSelectedVoice->IsStealable()) { |
1168 |
// remember which voice on which key on which engine channel we stole, so we can simply proceed on next voice stealing |
// remember which voice of which note on which key on which engine channel we stole, so we can simply proceed on next voice stealing |
1169 |
this->iuiLastStolenKeyGlobally = iuiSelectedKey; |
this->iuiLastStolenKeyGlobally = iuiSelectedKey; |
1170 |
|
this->itLastStolenNoteGlobally = itNote; |
1171 |
this->itLastStolenVoiceGlobally = itSelectedVoice; |
this->itLastStolenVoiceGlobally = itSelectedVoice; |
1172 |
this->pLastStolenChannel = pSelectedChannel; |
this->pLastStolenChannel = pSelectedChannel; |
1173 |
goto stealable_voice_found; // selection succeeded |
goto stealable_voice_found; // selection succeeded |
1174 |
} |
} |
|
++iuiSelectedKey; // get next key on current engine channel |
|
1175 |
} |
} |
1176 |
// get next engine channel |
++iuiSelectedKey; // get next key on current engine channel |
|
iChannelIndex = (iChannelIndex + 1) % engineChannels.size(); |
|
|
pSelectedChannel = static_cast<EngineChannelBase<V, R, I>*>(engineChannels[iChannelIndex]); |
|
|
|
|
|
#if CONFIG_DEVMODE |
|
|
if (pSelectedChannel == pBegin) { |
|
|
dmsg(1,("FATAL ERROR: voice stealing endless loop!\n")); |
|
|
dmsg(1,("VoiceSpawnsLeft=%d.\n", VoiceSpawnsLeft)); |
|
|
dmsg(1,("Exiting.\n")); |
|
|
exit(-1); |
|
|
} |
|
|
#endif // CONFIG_DEVMODE |
|
1177 |
} |
} |
1178 |
|
// get next engine channel |
1179 |
// jump point if a 'stealable' voice was found |
iChannelIndex = (iChannelIndex + 1) % engineChannels.size(); |
1180 |
stealable_voice_found: |
pSelectedChannel = static_cast<EngineChannelBase<V, R, I>*>(engineChannels[iChannelIndex]); |
1181 |
|
|
1182 |
#if CONFIG_DEVMODE |
#if CONFIG_DEVMODE |
1183 |
if (!itSelectedVoice->IsActive()) { |
if (pSelectedChannel == pBegin) { |
1184 |
dmsg(1,("EngineBase: ERROR, tried to steal a voice which was not active !!!\n")); |
dmsg(1,("FATAL ERROR: voice stealing endless loop!\n")); |
1185 |
return -1; |
dmsg(1,("VoiceSpawnsLeft=%d.\n", VoiceSpawnsLeft)); |
1186 |
|
dmsg(1,("Exiting.\n")); |
1187 |
|
exit(-1); |
1188 |
} |
} |
1189 |
#endif // CONFIG_DEVMODE |
#endif // CONFIG_DEVMODE |
1190 |
|
} |
1191 |
|
|
1192 |
// now kill the selected voice |
// jump point if a 'stealable' voice was found |
1193 |
itSelectedVoice->Kill(itNoteOnEvent); |
stealable_voice_found: |
|
|
|
|
--VoiceSpawnsLeft; |
|
1194 |
|
|
1195 |
return 0; // success |
#if CONFIG_DEVMODE |
1196 |
} |
if (!itSelectedVoice->IsActive()) { |
1197 |
else { |
dmsg(1,("EngineBase: ERROR, tried to steal a voice which was not active !!!\n")); |
|
dmsg(1,("Event pool emtpy!\n")); |
|
1198 |
return -1; |
return -1; |
1199 |
} |
} |
1200 |
|
#endif // CONFIG_DEVMODE |
1201 |
|
|
1202 |
|
// now kill the selected voice |
1203 |
|
itSelectedVoice->Kill(itNoteOnEvent); |
1204 |
|
|
1205 |
|
--VoiceSpawnsLeft; |
1206 |
|
|
1207 |
|
return 0; // success |
1208 |
} |
} |
1209 |
|
|
1210 |
void HandleInstrumentChanges() { |
void HandleInstrumentChanges() { |
1298 |
EngineChannelBase<V, R, I>* pEngineChannel = |
EngineChannelBase<V, R, I>* pEngineChannel = |
1299 |
static_cast<EngineChannelBase<V, R, I>*>(itVoiceStealEvent->pEngineChannel);; |
static_cast<EngineChannelBase<V, R, I>*>(itVoiceStealEvent->pEngineChannel);; |
1300 |
if (!pEngineChannel->pInstrument) continue; // ignore if no instrument loaded |
if (!pEngineChannel->pInstrument) continue; // ignore if no instrument loaded |
1301 |
|
|
1302 |
PoolVoiceIterator itNewVoice = |
PoolVoiceIterator itNewVoice = |
1303 |
LaunchVoice(pEngineChannel, itVoiceStealEvent, itVoiceStealEvent->Param.Note.Layer, itVoiceStealEvent->Param.Note.ReleaseTrigger, false, false); |
LaunchVoice(pEngineChannel, itVoiceStealEvent, itVoiceStealEvent->Param.Note.Layer, itVoiceStealEvent->Param.Note.ReleaseTrigger, false, false); |
1304 |
if (itNewVoice) { |
if (itNewVoice) { |
1305 |
|
// usually there should already be a new Note object |
1306 |
|
NoteIterator itNote = GetNotePool()->fromID(itVoiceStealEvent->Param.Note.ID); |
1307 |
|
if (!itNote) { // should not happen, but just to be sure ... |
1308 |
|
const note_id_t noteID = LaunchNewNote(pEngineChannel, &*itVoiceStealEvent); |
1309 |
|
if (!noteID) { |
1310 |
|
dmsg(1,("Engine: Voice stealing failed; No Note object and Note pool empty!\n")); |
1311 |
|
continue; |
1312 |
|
} |
1313 |
|
itNote = GetNotePool()->fromID(noteID); |
1314 |
|
} |
1315 |
|
// move voice from whereever it was, to the new note's list of active voices |
1316 |
|
itNewVoice = itNewVoice.moveToEndOf(itNote->pActiveVoices); |
1317 |
|
// render audio of this new voice for the first time |
1318 |
itNewVoice->Render(Samples); |
itNewVoice->Render(Samples); |
1319 |
if (itNewVoice->IsActive()) { // still active |
if (itNewVoice->IsActive()) { // still active |
1320 |
*(pEngineChannel->pRegionsInUse->allocAppend()) = itNewVoice->GetRegion(); |
*(pEngineChannel->pRegionsInUse->allocAppend()) = itNewVoice->GetRegion(); |
1586 |
EngineChannelBase<V, R, I>* pChannel = |
EngineChannelBase<V, R, I>* pChannel = |
1587 |
static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel); |
static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel); |
1588 |
|
|
|
//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 |
|
|
int k = itNoteOnEvent->Param.Note.Key + pChannel->GlobalTranspose; |
|
|
if (k < 0 || k > 127) return; //ignore keys outside the key range |
|
|
|
|
|
itNoteOnEvent->Param.Note.Key += pChannel->GlobalTranspose; |
|
|
int vel = itNoteOnEvent->Param.Note.Velocity; |
|
|
|
|
1589 |
const int key = itNoteOnEvent->Param.Note.Key; |
const int key = itNoteOnEvent->Param.Note.Key; |
1590 |
|
const int vel = itNoteOnEvent->Param.Note.Velocity; |
1591 |
|
if (key < 0 || key > 127) return; // ignore event, key outside allowed key range |
1592 |
|
|
1593 |
MidiKey* pKey = &pChannel->pMIDIKeyInfo[key]; |
MidiKey* pKey = &pChannel->pMIDIKeyInfo[key]; |
1594 |
|
|
1595 |
pChannel->listeners.PreProcessNoteOn(key, vel); |
pChannel->listeners.PreProcessNoteOn(key, vel); |
1617 |
if (pOtherKey->Active) { |
if (pOtherKey->Active) { |
1618 |
// get final portamento position of currently active voice |
// get final portamento position of currently active voice |
1619 |
if (pChannel->PortamentoMode) { |
if (pChannel->PortamentoMode) { |
1620 |
VoiceIterator itVoice = pOtherKey->pActiveVoices->last(); |
NoteIterator itNote = pOtherKey->pActiveNotes->last(); |
1621 |
if (itVoice) itVoice->UpdatePortamentoPos(itNoteOnEventOnKeyList); |
if (itNote) { |
1622 |
|
VoiceIterator itVoice = itNote->pActiveVoices->last(); |
1623 |
|
if (itVoice) itVoice->UpdatePortamentoPos(itNoteOnEventOnKeyList); |
1624 |
|
} |
1625 |
} |
} |
1626 |
// kill all voices on the (other) key |
// kill all voices on the (other) key |
1627 |
VoiceIterator itVoiceToBeKilled = pOtherKey->pActiveVoices->first(); |
for (NoteIterator itNote = pOtherKey->pActiveNotes->first(); itNote; ++itNote) { |
1628 |
VoiceIterator end = pOtherKey->pActiveVoices->end(); |
VoiceIterator itVoiceToBeKilled = itNote->pActiveVoices->first(); |
1629 |
for (; itVoiceToBeKilled != end; ++itVoiceToBeKilled) { |
VoiceIterator end = itNote->pActiveVoices->end(); |
1630 |
if (!(itVoiceToBeKilled->Type & Voice::type_release_trigger)) |
for (; itVoiceToBeKilled != end; ++itVoiceToBeKilled) { |
1631 |
itVoiceToBeKilled->Kill(itNoteOnEventOnKeyList); |
if (!(itVoiceToBeKilled->Type & Voice::type_release_trigger)) |
1632 |
|
itVoiceToBeKilled->Kill(itNoteOnEventOnKeyList); |
1633 |
|
} |
1634 |
} |
} |
1635 |
} |
} |
1636 |
} |
} |
1698 |
virtual void ProcessNoteOff(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNoteOffEvent) { |
virtual void ProcessNoteOff(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNoteOffEvent) { |
1699 |
EngineChannelBase<V, R, I>* pChannel = static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel); |
EngineChannelBase<V, R, I>* pChannel = static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel); |
1700 |
|
|
|
int k = itNoteOffEvent->Param.Note.Key + pChannel->GlobalTranspose; |
|
|
if (k < 0 || k > 127) return; //ignore keys outside the key range |
|
|
|
|
|
//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 |
|
|
itNoteOffEvent->Param.Note.Key += pChannel->GlobalTranspose; |
|
|
int vel = itNoteOffEvent->Param.Note.Velocity; |
|
|
|
|
1701 |
const int iKey = itNoteOffEvent->Param.Note.Key; |
const int iKey = itNoteOffEvent->Param.Note.Key; |
1702 |
|
const int vel = itNoteOffEvent->Param.Note.Velocity; |
1703 |
|
if (iKey < 0 || iKey > 127) return; // ignore event, key outside allowed key range |
1704 |
|
|
1705 |
MidiKey* pKey = &pChannel->pMIDIKeyInfo[iKey]; |
MidiKey* pKey = &pChannel->pMIDIKeyInfo[iKey]; |
1706 |
|
|
1707 |
pChannel->listeners.PreProcessNoteOff(iKey, vel); |
pChannel->listeners.PreProcessNoteOff(iKey, vel); |
1735 |
pChannel->SoloKey = i; |
pChannel->SoloKey = i; |
1736 |
// get final portamento position of currently active voice |
// get final portamento position of currently active voice |
1737 |
if (pChannel->PortamentoMode) { |
if (pChannel->PortamentoMode) { |
1738 |
VoiceIterator itVoice = pKey->pActiveVoices->first(); |
NoteIterator itNote = pKey->pActiveNotes->first(); |
1739 |
|
VoiceIterator itVoice = itNote->pActiveVoices->first(); |
1740 |
if (itVoice) itVoice->UpdatePortamentoPos(itNoteOffEventOnKeyList); |
if (itVoice) itVoice->UpdatePortamentoPos(itNoteOffEventOnKeyList); |
1741 |
} |
} |
1742 |
// create a pseudo note on event |
// create a pseudo note on event |
1748 |
itPseudoNoteOnEvent->Type = Event::type_note_on; |
itPseudoNoteOnEvent->Type = Event::type_note_on; |
1749 |
itPseudoNoteOnEvent->Param.Note.Key = i; |
itPseudoNoteOnEvent->Param.Note.Key = i; |
1750 |
itPseudoNoteOnEvent->Param.Note.Velocity = pOtherKey->Velocity; |
itPseudoNoteOnEvent->Param.Note.Velocity = pOtherKey->Velocity; |
1751 |
// allocate and trigger new voice(s) for the other key |
// assign a new note to this note-on event |
1752 |
TriggerNewVoices(pChannel, itPseudoNoteOnEvent, false); |
if (LaunchNewNote(pChannel, &*itPseudoNoteOnEvent)) { |
1753 |
|
// allocate and trigger new voice(s) for the other key |
1754 |
|
TriggerNewVoices(pChannel, itPseudoNoteOnEvent, false); |
1755 |
|
} |
1756 |
// if neither a voice was spawned or postponed then remove note on event from key again |
// if neither a voice was spawned or postponed then remove note on event from key again |
1757 |
if (!pOtherKey->Active && !pOtherKey->VoiceTheftsQueued) |
if (!pOtherKey->Active && !pOtherKey->VoiceTheftsQueued) |
1758 |
pOtherKey->pEvents->free(itPseudoNoteOnEvent); |
pOtherKey->pEvents->free(itPseudoNoteOnEvent); |
1765 |
if (bOtherKeysPressed) { |
if (bOtherKeysPressed) { |
1766 |
if (pKey->Active) { // kill all voices on this key |
if (pKey->Active) { // kill all voices on this key |
1767 |
bShouldRelease = false; // no need to release, as we kill it here |
bShouldRelease = false; // no need to release, as we kill it here |
1768 |
VoiceIterator itVoiceToBeKilled = pKey->pActiveVoices->first(); |
for (NoteIterator itNote = pKey->pActiveNotes->first(); itNote; ++itNote) { |
1769 |
VoiceIterator end = pKey->pActiveVoices->end(); |
VoiceIterator itVoiceToBeKilled = itNote->pActiveVoices->first(); |
1770 |
for (; itVoiceToBeKilled != end; ++itVoiceToBeKilled) { |
VoiceIterator end = itNote->pActiveVoices->end(); |
1771 |
if (!(itVoiceToBeKilled->Type & Voice::type_release_trigger)) |
for (; itVoiceToBeKilled != end; ++itVoiceToBeKilled) { |
1772 |
itVoiceToBeKilled->Kill(itNoteOffEventOnKeyList); |
if (!(itVoiceToBeKilled->Type & Voice::type_release_trigger)) |
1773 |
|
itVoiceToBeKilled->Kill(itNoteOffEventOnKeyList); |
1774 |
|
} |
1775 |
} |
} |
1776 |
} |
} |
1777 |
} else pChannel->PortamentoPos = -1.0f; |
} else pChannel->PortamentoPos = -1.0f; |
1783 |
|
|
1784 |
// spawn release triggered voice(s) if needed |
// spawn release triggered voice(s) if needed |
1785 |
if (pKey->ReleaseTrigger && pChannel->pInstrument) { |
if (pKey->ReleaseTrigger && pChannel->pInstrument) { |
1786 |
TriggerReleaseVoices(pChannel, itNoteOffEventOnKeyList); |
// assign a new note to this release event |
1787 |
|
if (LaunchNewNote(pChannel, &*itNoteOffEventOnKeyList)) { |
1788 |
|
// allocate and trigger new release voice(s) |
1789 |
|
TriggerReleaseVoices(pChannel, itNoteOffEventOnKeyList); |
1790 |
|
} |
1791 |
pKey->ReleaseTrigger = false; |
pKey->ReleaseTrigger = false; |
1792 |
} |
} |
1793 |
} |
} |
1816 |
pVoiceStealingQueue->clear(); |
pVoiceStealingQueue->clear(); |
1817 |
itLastStolenVoice = VoiceIterator(); |
itLastStolenVoice = VoiceIterator(); |
1818 |
itLastStolenVoiceGlobally = VoiceIterator(); |
itLastStolenVoiceGlobally = VoiceIterator(); |
1819 |
|
itLastStolenNote = NoteIterator(); |
1820 |
|
itLastStolenNoteGlobally = NoteIterator(); |
1821 |
iuiLastStolenKey = RTList<uint>::Iterator(); |
iuiLastStolenKey = RTList<uint>::Iterator(); |
1822 |
iuiLastStolenKeyGlobally = RTList<uint>::Iterator(); |
iuiLastStolenKeyGlobally = RTList<uint>::Iterator(); |
1823 |
pLastStolenChannel = NULL; |
pLastStolenChannel = NULL; |
1824 |
|
|
1825 |
|
// reset all notes |
1826 |
|
pNotePool->clear(); |
1827 |
|
for (NoteIterator itNote = pNotePool->allocAppend(); itNote; |
1828 |
|
itNote = pNotePool->allocAppend()) |
1829 |
|
{ |
1830 |
|
itNote->reset(); |
1831 |
|
} |
1832 |
|
pNotePool->clear(); |
1833 |
|
|
1834 |
// reset all voices |
// reset all voices |
1835 |
|
pVoicePool->clear(); |
1836 |
for (VoiceIterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) { |
for (VoiceIterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) { |
1837 |
iterVoice->Reset(); |
iterVoice->Reset(); |
1838 |
} |
} |
1916 |
// launch the new voice |
// launch the new voice |
1917 |
if (itNewVoice->Trigger(pChannel, itNoteOnEvent, pChannel->Pitch, pRegion, VoiceType, iKeyGroup) < 0) { |
if (itNewVoice->Trigger(pChannel, itNoteOnEvent, pChannel->Pitch, pRegion, VoiceType, iKeyGroup) < 0) { |
1918 |
dmsg(4,("Voice not triggered\n")); |
dmsg(4,("Voice not triggered\n")); |
1919 |
pKey->pActiveVoices->free(itNewVoice); |
GetVoicePool()->free(itNewVoice); |
1920 |
} |
} |
1921 |
else { // on success |
else { // on success |
1922 |
--VoiceSpawnsLeft; |
--VoiceSpawnsLeft; |
1965 |
} |
} |
1966 |
|
|
1967 |
private: |
private: |
1968 |
|
Pool< Note<V> >* pNotePool; |
1969 |
|
Pool<note_id_t> noteIDPool; |
1970 |
Pool<V>* pVoicePool; ///< Contains all voices that can be activated. |
Pool<V>* pVoicePool; ///< Contains all voices that can be activated. |
1971 |
Pool<RR*> SuspendedRegions; |
Pool<RR*> SuspendedRegions; |
1972 |
Mutex SuspendedRegionsMutex; |
Mutex SuspendedRegionsMutex; |