23 |
#include <sstream> |
#include <sstream> |
24 |
#include "DiskThread.h" |
#include "DiskThread.h" |
25 |
#include "Voice.h" |
#include "Voice.h" |
26 |
|
#include "EGADSR.h" |
27 |
|
|
28 |
#include "Engine.h" |
#include "Engine.h" |
29 |
|
|
314 |
this->MaxSamplesPerCycle = pAudioOutputDevice->MaxSamplesPerCycle(); |
this->MaxSamplesPerCycle = pAudioOutputDevice->MaxSamplesPerCycle(); |
315 |
this->SampleRate = pAudioOutputDevice->SampleRate(); |
this->SampleRate = pAudioOutputDevice->SampleRate(); |
316 |
|
|
317 |
|
// FIXME: audio drivers with varying fragment sizes might be a problem here |
318 |
|
MaxFadeOutPos = MaxSamplesPerCycle - int(double(SampleRate) * EG_MIN_RELEASE_TIME) - 1; |
319 |
|
if (MaxFadeOutPos < 0) |
320 |
|
throw LinuxSamplerException("EG_MIN_RELEASE_TIME in EGADSR.h to big for current audio fragment size / sampling rate!"); |
321 |
|
|
322 |
// (re)create disk thread |
// (re)create disk thread |
323 |
if (this->pDiskThread) { |
if (this->pDiskThread) { |
324 |
this->pDiskThread->StopThread(); |
this->pDiskThread->StopThread(); |
472 |
itVoice->Render(Samples); |
itVoice->Render(Samples); |
473 |
if (itVoice->IsActive()) active_voices++; // still active |
if (itVoice->IsActive()) active_voices++; // still active |
474 |
else { // voice reached end, is now inactive |
else { // voice reached end, is now inactive |
475 |
KillVoiceImmediately(itVoice); // remove voice from the list of active voices |
FreeVoice(itVoice); // remove voice from the list of active voices |
476 |
} |
} |
477 |
} |
} |
478 |
} |
} |
486 |
for (; itVoiceStealEvent != end; ++itVoiceStealEvent) { |
for (; itVoiceStealEvent != end; ++itVoiceStealEvent) { |
487 |
Pool<Voice>::Iterator itNewVoice = LaunchVoice(itVoiceStealEvent, itVoiceStealEvent->Param.Note.Layer, itVoiceStealEvent->Param.Note.ReleaseTrigger, false); |
Pool<Voice>::Iterator itNewVoice = LaunchVoice(itVoiceStealEvent, itVoiceStealEvent->Param.Note.Layer, itVoiceStealEvent->Param.Note.ReleaseTrigger, false); |
488 |
if (itNewVoice) { |
if (itNewVoice) { |
489 |
itNewVoice->Render(Samples); |
for (; itNewVoice; itNewVoice = itNewVoice->itChildVoice) { |
490 |
if (itNewVoice->IsActive()) active_voices++; // still active |
itNewVoice->Render(Samples); |
491 |
else { // voice reached end, is now inactive |
if (itNewVoice->IsActive()) active_voices++; // still active |
492 |
KillVoiceImmediately(itNewVoice); // remove voice from the list of active voices |
else { // voice reached end, is now inactive |
493 |
|
FreeVoice(itNewVoice); // remove voice from the list of active voices |
494 |
|
} |
495 |
} |
} |
496 |
} |
} |
497 |
else dmsg(1,("Ouch, voice stealing didn't work out!\n")); |
else dmsg(1,("Ouch, voice stealing didn't work out!\n")); |
724 |
return itNewVoice; // success |
return itNewVoice; // success |
725 |
} |
} |
726 |
} |
} |
727 |
else if (VoiceStealing) StealVoice(itNoteOnEvent, iLayer, ReleaseTriggerVoice); // no free voice left, so steal one |
else if (VoiceStealing) { |
728 |
|
// first, get total amount of required voices (dependant on amount of layers) |
729 |
|
::gig::Region* pRegion = pInstrument->GetRegion(itNoteOnEvent->Param.Note.Key); |
730 |
|
if (!pRegion) return Pool<Voice>::Iterator(); // nothing defined for this MIDI key, so no voice needed |
731 |
|
int voicesRequired = pRegion->Layers; |
732 |
|
|
733 |
|
// now steal the (remaining) amount of voices |
734 |
|
for (int i = iLayer; i < voicesRequired; i++) |
735 |
|
StealVoice(itNoteOnEvent); |
736 |
|
|
737 |
|
// put note-on event into voice-stealing queue, so it will be reprocessed after killed voice died |
738 |
|
RTList<Event>::Iterator itStealEvent = pVoiceStealingQueue->allocAppend(); |
739 |
|
if (itStealEvent) { |
740 |
|
*itStealEvent = *itNoteOnEvent; // copy event |
741 |
|
itStealEvent->Param.Note.Layer = iLayer; |
742 |
|
itStealEvent->Param.Note.ReleaseTrigger = ReleaseTriggerVoice; |
743 |
|
} |
744 |
|
else dmsg(1,("Voice stealing queue full!\n")); |
745 |
|
} |
746 |
|
|
747 |
return Pool<Voice>::Iterator(); // no free voice or error |
return Pool<Voice>::Iterator(); // no free voice or error |
748 |
} |
} |
753 |
* voice stealing and postpone the note-on event until the selected |
* voice stealing and postpone the note-on event until the selected |
754 |
* voice actually died. |
* voice actually died. |
755 |
* |
* |
756 |
* @param itNoteOnEvent - key, velocity and time stamp of the event |
* @param itNoteOnEvent - key, velocity and time stamp of the event |
|
* @param iLayer - layer index for the new voice |
|
|
* @param ReleaseTriggerVoice - if new voice is a release triggered voice |
|
757 |
*/ |
*/ |
758 |
void Engine::StealVoice(Pool<Event>::Iterator& itNoteOnEvent, int iLayer, bool ReleaseTriggerVoice) { |
void Engine::StealVoice(Pool<Event>::Iterator& itNoteOnEvent) { |
759 |
if (!pEventPool->poolIsEmpty()) { |
if (!pEventPool->poolIsEmpty()) { |
760 |
|
|
761 |
RTList<uint>::Iterator iuiOldestKey; |
RTList<uint>::Iterator iuiOldestKey; |
797 |
midi_key_info_t* pOldestKey = &pMIDIKeyInfo[*iuiOldestKey]; |
midi_key_info_t* pOldestKey = &pMIDIKeyInfo[*iuiOldestKey]; |
798 |
itOldestVoice = pOldestKey->pActiveVoices->first(); |
itOldestVoice = pOldestKey->pActiveVoices->first(); |
799 |
} |
} |
800 |
else { // too less voices, even for voice stealing |
else { |
801 |
dmsg(1,("Voice overflow! - You might recompile with higher MAX_AUDIO_VOICES!\n")); |
dmsg(1,("gig::Engine: Warning, too less voices, even for voice stealing! - Better recompile with higher MAX_AUDIO_VOICES.\n")); |
802 |
return; |
return; |
803 |
} |
} |
804 |
} |
} |
825 |
// remember which voice on which key we stole, so we can simply proceed for the next voice stealing |
// remember which voice on which key we stole, so we can simply proceed for the next voice stealing |
826 |
this->itLastStolenVoice = itOldestVoice; |
this->itLastStolenVoice = itOldestVoice; |
827 |
this->iuiLastStolenKey = iuiOldestKey; |
this->iuiLastStolenKey = iuiOldestKey; |
|
// put note-on event into voice-stealing queue, so it will be reprocessed after killed voice died |
|
|
RTList<Event>::Iterator itStealEvent = pVoiceStealingQueue->allocAppend(); |
|
|
if (itStealEvent) { |
|
|
*itStealEvent = *itNoteOnEvent; // copy event |
|
|
itStealEvent->Param.Note.Layer = iLayer; |
|
|
itStealEvent->Param.Note.ReleaseTrigger = ReleaseTriggerVoice; |
|
|
} |
|
|
else dmsg(1,("Voice stealing queue full!\n")); |
|
828 |
} |
} |
829 |
else dmsg(1,("Event pool emtpy!\n")); |
else dmsg(1,("Event pool emtpy!\n")); |
830 |
} |
} |
831 |
|
|
832 |
/** |
/** |
833 |
* Immediately kills the voice given with pVoice (no matter if sustain is |
* Removes the given voice from the MIDI key's list of active voices. |
834 |
* pressed or not) and removes it from the MIDI key's list of active voice. |
* This method will be called when a voice went inactive, e.g. because |
835 |
* This method will e.g. be called if a voice went inactive by itself. |
* it finished to playback its sample, finished its release stage or |
836 |
|
* just was killed. |
837 |
* |
* |
838 |
* @param itVoice - points to the voice to be killed |
* @param itVoice - points to the voice to be freed |
839 |
*/ |
*/ |
840 |
void Engine::KillVoiceImmediately(Pool<Voice>::Iterator& itVoice) { |
void Engine::FreeVoice(Pool<Voice>::Iterator& itVoice) { |
841 |
if (itVoice) { |
if (itVoice) { |
|
if (itVoice->IsActive()) itVoice->KillImmediately(); |
|
|
|
|
842 |
midi_key_info_t* pKey = &pMIDIKeyInfo[itVoice->MIDIKey]; |
midi_key_info_t* pKey = &pMIDIKeyInfo[itVoice->MIDIKey]; |
843 |
|
|
844 |
uint keygroup = itVoice->KeyGroup; |
uint keygroup = itVoice->KeyGroup; |
860 |
dmsg(3,("Key has no more voices now\n")); |
dmsg(3,("Key has no more voices now\n")); |
861 |
} |
} |
862 |
} |
} |
863 |
else std::cerr << "Couldn't release voice! (pVoice == NULL)\n" << std::flush; |
else std::cerr << "Couldn't release voice! (!itVoice)\n" << std::flush; |
864 |
} |
} |
865 |
|
|
866 |
/** |
/** |
1110 |
} |
} |
1111 |
|
|
1112 |
String Engine::Version() { |
String Engine::Version() { |
1113 |
String s = "$Revision: 1.15 $"; |
String s = "$Revision: 1.16 $"; |
1114 |
return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword |
return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword |
1115 |
} |
} |
1116 |
|
|