--- linuxsampler/trunk/src/engines/gig/Engine.cpp 2004/10/08 20:51:39 271 +++ linuxsampler/trunk/src/engines/gig/Engine.cpp 2004/10/16 17:38:03 287 @@ -23,6 +23,7 @@ #include #include "DiskThread.h" #include "Voice.h" +#include "EGADSR.h" #include "Engine.h" @@ -313,6 +314,11 @@ this->MaxSamplesPerCycle = pAudioOutputDevice->MaxSamplesPerCycle(); this->SampleRate = pAudioOutputDevice->SampleRate(); + // FIXME: audio drivers with varying fragment sizes might be a problem here + MaxFadeOutPos = MaxSamplesPerCycle - int(double(SampleRate) * EG_MIN_RELEASE_TIME) - 1; + if (MaxFadeOutPos < 0) + throw LinuxSamplerException("EG_MIN_RELEASE_TIME in EGADSR.h to big for current audio fragment size / sampling rate!"); + // (re)create disk thread if (this->pDiskThread) { this->pDiskThread->StopThread(); @@ -466,7 +472,7 @@ itVoice->Render(Samples); if (itVoice->IsActive()) active_voices++; // still active else { // voice reached end, is now inactive - KillVoiceImmediately(itVoice); // remove voice from the list of active voices + FreeVoice(itVoice); // remove voice from the list of active voices } } } @@ -480,13 +486,15 @@ for (; itVoiceStealEvent != end; ++itVoiceStealEvent) { Pool::Iterator itNewVoice = LaunchVoice(itVoiceStealEvent, itVoiceStealEvent->Param.Note.Layer, itVoiceStealEvent->Param.Note.ReleaseTrigger, false); if (itNewVoice) { - itNewVoice->Render(Samples); - if (itNewVoice->IsActive()) active_voices++; // still active - else { // voice reached end, is now inactive - KillVoiceImmediately(itNewVoice); // remove voice from the list of active voices + for (; itNewVoice; itNewVoice = itNewVoice->itChildVoice) { + itNewVoice->Render(Samples); + if (itNewVoice->IsActive()) active_voices++; // still active + else { // voice reached end, is now inactive + FreeVoice(itNewVoice); // remove voice from the list of active voices + } } } - else dmsg(1,("Ouch, voice stealing didn't work out!\n")); + else dmsg(1,("gig::Engine: ERROR, voice stealing didn't work out!\n")); } } // reset voice stealing for the new fragment @@ -495,6 +503,29 @@ iuiLastStolenKey = RTList::Iterator(); + // free all keys which have no active voices left + { + RTList::Iterator iuiKey = pActiveKeys->first(); + RTList::Iterator end = pActiveKeys->end(); + while (iuiKey != end) { // iterate through all active keys + midi_key_info_t* pKey = &pMIDIKeyInfo[*iuiKey]; + ++iuiKey; + if (pKey->pActiveVoices->isEmpty()) FreeKey(pKey); + #if DEVMODE + else { // FIXME: should be removed before the final release (purpose: just a sanity check for debugging) + RTList::Iterator itVoice = pKey->pActiveVoices->first(); + RTList::Iterator itVoicesEnd = 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")); + } + } + } + #endif // DEVMODE + } + } + + // write that to the disk thread class so that it can print it // on the console for debugging purposes ActiveVoiceCount = active_voices; @@ -621,7 +652,7 @@ RTList::Iterator itNoteOnEventOnKeyList = itNoteOnEvent.moveToEndOf(pKey->pEvents); // allocate and trigger a new voice for the key - LaunchVoice(itNoteOnEventOnKeyList); + LaunchVoice(itNoteOnEventOnKeyList, 0, false, true); } /** @@ -647,7 +678,7 @@ // spawn release triggered voice(s) if needed if (pKey->ReleaseTrigger) { - LaunchVoice(itNoteOffEventOnKeyList, 0, true); + LaunchVoice(itNoteOffEventOnKeyList, 0, true, false); //FIXME: for the moment we don't perform voice stealing for release triggered samples pKey->ReleaseTrigger = false; } } @@ -686,7 +717,7 @@ Pool::Iterator itNewVoice = pKey->pActiveVoices->allocAppend(); if (itNewVoice) { // launch the new voice - if (itNewVoice->Trigger(itNoteOnEvent, this->Pitch, this->pInstrument, iLayer, ReleaseTriggerVoice) < 0) { + if (itNewVoice->Trigger(itNoteOnEvent, this->Pitch, this->pInstrument, iLayer, ReleaseTriggerVoice, VoiceStealing) < 0) { dmsg(1,("Triggering new voice failed!\n")); pKey->pActiveVoices->free(itNewVoice); } @@ -716,7 +747,25 @@ return itNewVoice; // success } } - else if (VoiceStealing) StealVoice(itNoteOnEvent, iLayer, ReleaseTriggerVoice); // no free voice left, so steal one + else if (VoiceStealing) { + // first, get total amount of required voices (dependant on amount of layers) + ::gig::Region* pRegion = pInstrument->GetRegion(itNoteOnEvent->Param.Note.Key); + if (!pRegion) return Pool::Iterator(); // nothing defined for this MIDI key, so no voice needed + int voicesRequired = pRegion->Layers; + + // now steal the (remaining) amount of voices + for (int i = iLayer; i < voicesRequired; i++) + StealVoice(itNoteOnEvent); + + // put note-on event into voice-stealing queue, so it will be reprocessed after killed voice died + RTList::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")); + } return Pool::Iterator(); // no free voice or error } @@ -727,11 +776,9 @@ * voice stealing and postpone the note-on event until the selected * voice actually died. * - * @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 + * @param itNoteOnEvent - key, velocity and time stamp of the event */ - void Engine::StealVoice(Pool::Iterator& itNoteOnEvent, int iLayer, bool ReleaseTriggerVoice) { + void Engine::StealVoice(Pool::Iterator& itNoteOnEvent) { if (!pEventPool->poolIsEmpty()) { RTList::Iterator iuiOldestKey; @@ -773,8 +820,8 @@ midi_key_info_t* pOldestKey = &pMIDIKeyInfo[*iuiOldestKey]; itOldestVoice = pOldestKey->pActiveVoices->first(); } - else { // too less voices, even for voice stealing - dmsg(1,("Voice overflow! - You might recompile with higher MAX_AUDIO_VOICES!\n")); + else { + dmsg(1,("gig::Engine: Warning, too less voices, even for voice stealing! - Better recompile with higher MAX_AUDIO_VOICES.\n")); return; } } @@ -796,34 +843,28 @@ } } + //FIXME: can be removed, just a sanity check for debugging + if (!itOldestVoice->IsActive()) dmsg(1,("gig::Engine: ERROR, tried to steal a voice which was not active !!!\n")); + // now kill the selected voice itOldestVoice->Kill(itNoteOnEvent); // remember which voice on which key we stole, so we can simply proceed for the next voice stealing this->itLastStolenVoice = itOldestVoice; this->iuiLastStolenKey = iuiOldestKey; - // put note-on event into voice-stealing queue, so it will be reprocessed after killed voice died - RTList::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")); } else dmsg(1,("Event pool emtpy!\n")); } /** - * Immediately kills the voice given with pVoice (no matter if sustain is - * pressed or not) and removes it from the MIDI key's list of active voice. - * This method will e.g. be called if a voice went inactive by itself. + * Removes the given voice from the MIDI key's list of active voices. + * This method will be called when a voice went inactive, e.g. because + * it finished to playback its sample, finished its release stage or + * just was killed. * - * @param itVoice - points to the voice to be killed + * @param itVoice - points to the voice to be freed */ - void Engine::KillVoiceImmediately(Pool::Iterator& itVoice) { + void Engine::FreeVoice(Pool::Iterator& itVoice) { if (itVoice) { - if (itVoice->IsActive()) itVoice->KillImmediately(); - midi_key_info_t* pKey = &pMIDIKeyInfo[itVoice->MIDIKey]; uint keygroup = itVoice->KeyGroup; @@ -831,21 +872,31 @@ // free the voice object pVoicePool->free(itVoice); - // check if there are no voices left on the MIDI key and update the key info if so - if (pKey->pActiveVoices->isEmpty()) { - if (keygroup) { // if voice / key belongs to a key group - uint** ppKeyGroup = &ActiveKeyGroups[keygroup]; - if (*ppKeyGroup == &*pKey->itSelf) *ppKeyGroup = NULL; // remove key from key group - } - pKey->Active = false; - pActiveKeys->free(pKey->itSelf); // remove key from list of active keys - pKey->itSelf = RTList::Iterator(); - pKey->ReleaseTrigger = false; - pKey->pEvents->clear(); - dmsg(3,("Key has no more voices now\n")); + // if no other voices left and member of a key group, remove from key group + if (pKey->pActiveVoices->isEmpty() && keygroup) { + uint** ppKeyGroup = &ActiveKeyGroups[keygroup]; + if (*ppKeyGroup == &*pKey->itSelf) *ppKeyGroup = NULL; // remove key from key group } } - else std::cerr << "Couldn't release voice! (pVoice == NULL)\n" << std::flush; + else std::cerr << "Couldn't release voice! (!itVoice)\n" << std::flush; + } + + /** + * Called when there's no more voice left on a key, this call will + * update the key info respectively. + * + * @param pKey - key which is now inactive + */ + void Engine::FreeKey(midi_key_info_t* pKey) { + if (pKey->pActiveVoices->isEmpty()) { + pKey->Active = false; + pActiveKeys->free(pKey->itSelf); // remove key from list of active keys + pKey->itSelf = RTList::Iterator(); + pKey->ReleaseTrigger = false; + pKey->pEvents->clear(); + dmsg(3,("Key has no more voices now\n")); + } + else dmsg(1,("gig::Engine: Oops, tried to free a key which contains voices.\n")); } /** @@ -1095,7 +1146,7 @@ } String Engine::Version() { - String s = "$Revision: 1.15 $"; + String s = "$Revision: 1.17 $"; return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword }