--- linuxsampler/trunk/src/engines/gig/Engine.cpp 2005/03/15 19:27:01 466 +++ linuxsampler/trunk/src/engines/gig/Engine.cpp 2005/06/06 16:54:20 614 @@ -96,14 +96,17 @@ else dmsg(4,("This gig::Engine has now %d EngineChannels.\n",pEngine->engineChannels.size())); } + /** + * Constructor + */ Engine::Engine() { pAudioOutputDevice = NULL; pDiskThread = NULL; pEventGenerator = NULL; - pSysexBuffer = new RingBuffer(SYSEX_BUFFER_SIZE, 0); - pEventQueue = new RingBuffer(MAX_EVENTS_PER_FRAGMENT, 0); - pEventPool = new Pool(MAX_EVENTS_PER_FRAGMENT); - pVoicePool = new Pool(MAX_AUDIO_VOICES); + pSysexBuffer = new RingBuffer(CONFIG_SYSEX_BUFFER_SIZE, 0); + pEventQueue = new RingBuffer(CONFIG_MAX_EVENTS_PER_FRAGMENT, 0); + pEventPool = new Pool(CONFIG_MAX_EVENTS_PER_FRAGMENT); + pVoicePool = new Pool(CONFIG_MAX_VOICES); pVoiceStealingQueue = new RTList(pEventPool); pGlobalEvents = new RTList(pEventPool); for (RTList::Iterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) { @@ -118,6 +121,9 @@ ResetInternal(); } + /** + * Destructor + */ Engine::~Engine() { if (pDiskThread) { dmsg(1,("Stopping disk thread...")); @@ -198,6 +204,15 @@ pEventQueue->init(); } + /** + * Connect this engine instance with the given audio output device. + * This method will be called when an Engine instance is created. + * All of the engine's data structures which are dependant to the used + * audio output device / driver will be (re)allocated and / or + * adjusted appropriately. + * + * @param pAudioOut - audio output device to connect to + */ void Engine::Connect(AudioOutputDevice* pAudioOut) { pAudioOutputDevice = pAudioOut; @@ -216,9 +231,9 @@ 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; + MaxFadeOutPos = MaxSamplesPerCycle - int(double(SampleRate) * CONFIG_EG_MIN_RELEASE_TIME) - 1; if (MaxFadeOutPos < 0) - throw LinuxSamplerException("EG_MIN_RELEASE_TIME in EGADSR.h too big for current audio fragment size / sampling rate!"); + throw LinuxSamplerException("CONFIG_EG_MIN_RELEASE_TIME too big for current audio fragment size / sampling rate!"); // (re)create disk thread if (this->pDiskThread) { @@ -227,7 +242,7 @@ delete this->pDiskThread; dmsg(1,("OK\n")); } - this->pDiskThread = new DiskThread(((pAudioOut->MaxSamplesPerCycle() << MAX_PITCH) << 1) + 6); //FIXME: assuming stereo + this->pDiskThread = new DiskThread(((pAudioOut->MaxSamplesPerCycle() << CONFIG_MAX_PITCH) << 1) + 6); //FIXME: assuming stereo if (!pDiskThread) { dmsg(0,("gig::Engine new diskthread = NULL\n")); exit(EXIT_FAILURE); @@ -272,6 +287,9 @@ } } + /** + * Clear all engine global event lists. + */ void Engine::ClearEventLists() { pGlobalEvents->clear(); } @@ -352,10 +370,10 @@ } } - // We only allow a maximum of MAX_AUDIO_VOICES voices to be stolen + // We only allow a maximum of CONFIG_MAX_VOICES voices to be stolen // in each audio fragment. All subsequent request for spawning new // voices in the same audio fragment will be ignored. - VoiceTheftsLeft = MAX_AUDIO_VOICES; + VoiceTheftsLeft = CONFIG_MAX_VOICES; // reset internal voice counter (just for statistic of active voices) ActiveVoiceCountTemp = 0; @@ -399,6 +417,15 @@ return 0; } + /** + * Dispatch and handle all events in this audio fragment for the given + * engine channel. + * + * @param pEngineChannel - engine channel on which events should be + * processed + * @param Samples - amount of sample points to be processed in + * this audio fragment cycle + */ void Engine::ProcessEvents(EngineChannel* pEngineChannel, uint Samples) { // get all events from the engine channels's input event queue which belong to the current fragment // (these are the common events like NoteOn, NoteOff, ControlChange, etc.) @@ -431,6 +458,15 @@ } } + /** + * Render all 'normal' voices (that is voices which were not stolen in + * this fragment) on the given engine channel. + * + * @param pEngineChannel - engine channel on which audio should be + * rendered + * @param Samples - amount of sample points to be rendered in + * this audio fragment cycle + */ void Engine::RenderActiveVoices(EngineChannel* pEngineChannel, uint Samples) { RTList::Iterator iuiKey = pEngineChannel->pActiveKeys->first(); RTList::Iterator end = pEngineChannel->pActiveKeys->end(); @@ -451,6 +487,19 @@ } } + /** + * Render all stolen voices (only voices which were stolen in this + * fragment) on the given engine channel. Stolen voices are rendered + * after all normal voices have been rendered; this is needed to render + * audio of those voices which were selected for voice stealing until + * the point were the stealing (that is the take over of the voice) + * actually happened. + * + * @param pEngineChannel - engine channel on which audio should be + * rendered + * @param Samples - amount of sample points to be rendered in + * this audio fragment cycle + */ void Engine::RenderStolenVoices(uint Samples) { RTList::Iterator itVoiceStealEvent = pVoiceStealingQueue->first(); RTList::Iterator end = pVoiceStealingQueue->end(); @@ -466,9 +515,21 @@ } } else dmsg(1,("gig::Engine: ERROR, voice stealing didn't work out!\n")); + + // we need to clear the key's event list explicitly here in case key was never active + midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[itVoiceStealEvent->Param.Note.Key]; + pKey->VoiceTheftsQueued--; + if (!pKey->Active && !pKey->VoiceTheftsQueued) pKey->pEvents->clear(); } } + /** + * Free all keys which have turned inactive in this audio fragment, from + * the list of active keys and clear all event lists on that engine + * channel. + * + * @param pEngineChannel - engine channel to cleanup + */ void Engine::PostProcess(EngineChannel* pEngineChannel) { // free all keys which have no active voices left { @@ -478,8 +539,8 @@ midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[*iuiKey]; ++iuiKey; if (pKey->pActiveVoices->isEmpty()) FreeKey(pEngineChannel, pKey); - #if DEVMODE - else { // FIXME: should be removed before the final release (purpose: just a sanity check for debugging) + #if CONFIG_DEVMODE + else { // 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 @@ -488,7 +549,7 @@ } } } - #endif // DEVMODE + #endif // CONFIG_DEVMODE } } @@ -523,7 +584,7 @@ // finally place sysex event into input event queue pEventQueue->push(&event); } - else dmsg(1,("Engine: Sysex message too large (%d byte) for input buffer (%d byte)!",Size,SYSEX_BUFFER_SIZE)); + else dmsg(1,("Engine: Sysex message too large (%d byte) for input buffer (%d byte)!",Size,CONFIG_SYSEX_BUFFER_SIZE)); } else dmsg(1,("Engine: Input event queue full!")); } @@ -575,6 +636,10 @@ } } + // if neither a voice was spawned or postponed then remove note on event from key again + if (!pKey->Active && !pKey->VoiceTheftsQueued) + pKey->pEvents->free(itNoteOnEventOnKeyList); + pKey->RoundRobinIndex++; } @@ -595,22 +660,26 @@ // release voices on this key if needed if (pKey->Active && !pEngineChannel->SustainPedal) { itNoteOffEvent->Type = Event::type_release; // transform event type - } - // move event to the key's own event list - RTList::Iterator itNoteOffEventOnKeyList = itNoteOffEvent.moveToEndOf(pKey->pEvents); + // move event to the key's own event list + RTList::Iterator itNoteOffEventOnKeyList = itNoteOffEvent.moveToEndOf(pKey->pEvents); - // spawn release triggered voice(s) if needed - if (pKey->ReleaseTrigger) { - // first, get total amount of required voices (dependant on amount of layers) - ::gig::Region* pRegion = pEngineChannel->pInstrument->GetRegion(itNoteOffEventOnKeyList->Param.Note.Key); - if (pRegion) { - int voicesRequired = pRegion->Layers; - // now launch the required amount of voices - for (int i = 0; i < voicesRequired; i++) - LaunchVoice(pEngineChannel, itNoteOffEventOnKeyList, i, true, false); //FIXME: for the moment we don't perform voice stealing for release triggered samples + // spawn release triggered voice(s) if needed + if (pKey->ReleaseTrigger && itNoteOffEventOnKeyList->Param.Note.Velocity) { + // first, get total amount of required voices (dependant on amount of layers) + ::gig::Region* pRegion = pEngineChannel->pInstrument->GetRegion(itNoteOffEventOnKeyList->Param.Note.Key); + if (pRegion) { + int voicesRequired = pRegion->Layers; + // now launch the required amount of voices + for (int i = 0; i < voicesRequired; i++) + LaunchVoice(pEngineChannel, itNoteOffEventOnKeyList, i, true, false); //FIXME: for the moment we don't perform voice stealing for release triggered samples + } + pKey->ReleaseTrigger = false; } - pKey->ReleaseTrigger = false; + + // if neither a voice was spawned or postponed then remove note off event from key again + if (!pKey->Active && !pKey->VoiceTheftsQueued) + pKey->pEvents->free(itNoteOffEventOnKeyList); } } @@ -682,18 +751,19 @@ } } else if (VoiceStealing) { - // try to steal one voice - StealVoice(pEngineChannel, 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; + int result = StealVoice(pEngineChannel, itNoteOnEvent); + if (!result) { // voice stolen successfully + // 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; + pKey->VoiceTheftsQueued++; + } + else dmsg(1,("Voice stealing queue full!\n")); } - else dmsg(1,("Voice stealing queue full!\n")); } return Pool::Iterator(); // no free voice or error @@ -707,41 +777,35 @@ * * @param pEngineChannel - engine channel on which this event occured on * @param itNoteOnEvent - key, velocity and time stamp of the event + * @returns 0 on success, a value < 0 if no active voice could be picked for voice stealing */ - void Engine::StealVoice(EngineChannel* pEngineChannel, Pool::Iterator& itNoteOnEvent) { + int Engine::StealVoice(EngineChannel* pEngineChannel, Pool::Iterator& itNoteOnEvent) { if (!VoiceTheftsLeft) { - dmsg(1,("Max. voice thefts per audio fragment reached (you may raise MAX_AUDIO_VOICES).\n")); - return; + dmsg(1,("Max. voice thefts per audio fragment reached (you may raise CONFIG_MAX_VOICES).\n")); + return -1; } if (!pEventPool->poolIsEmpty()) { RTList::Iterator itSelectedVoice; // Select one voice for voice stealing - switch (VOICE_STEAL_ALGORITHM) { + switch (CONFIG_VOICE_STEAL_ALGO) { // try to pick the oldest voice on the key where the new // voice should be spawned, if there is no voice on that - // key, or no voice left to kill there, then procceed with + // key, or no voice left to kill, then procceed with // 'oldestkey' algorithm case voice_steal_algo_oldestvoiceonkey: { - #if 0 // FIXME: broken midi_key_info_t* pSelectedKey = &pEngineChannel->pMIDIKeyInfo[itNoteOnEvent->Param.Note.Key]; - if (this->itLastStolenVoice) { - itSelectedVoice = this->itLastStolenVoice; - ++itSelectedVoice; - } - else { // no voice stolen in this audio fragment cycle yet - itSelectedVoice = pSelectedKey->pActiveVoices->first(); - } - if (itSelectedVoice) { - iuiSelectedKey = pSelectedKey->itSelf; - break; // selection succeeded - } - #endif + itSelectedVoice = pSelectedKey->pActiveVoices->first(); + // proceed iterating if voice was created in this fragment cycle + while (itSelectedVoice && !itSelectedVoice->hasRendered()) ++itSelectedVoice; + // if we haven't found a voice then proceed with algorithm 'oldestkey' + if (itSelectedVoice && itSelectedVoice->hasRendered()) break; } // 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 (this->itLastStolenVoice) { @@ -764,11 +828,12 @@ case voice_steal_algo_none: default: { dmsg(1,("No free voice (voice stealing disabled)!\n")); - return; + return -1; } } - // steal oldest voice on the oldest key from this or any other engine channel + // if we couldn't steal a voice from the same engine channel then + // steal oldest voice on the oldest key from any other engine channel if (!itSelectedVoice) { EngineChannel* pSelectedChannel = (pLastStolenChannel) ? pLastStolenChannel : pEngineChannel; int iChannelIndex = pSelectedChannel->iEngineIndexSelf; @@ -786,8 +851,12 @@ } } - //FIXME: can be removed, just a sanity check for debugging - if (!itSelectedVoice->IsActive()) dmsg(1,("gig::Engine: ERROR, tried to steal a voice which was not active !!!\n")); + #if CONFIG_DEVMODE + if (!itSelectedVoice->IsActive()) { + dmsg(1,("gig::Engine: ERROR, tried to steal a voice which was not active !!!\n")); + return -1; + } + #endif // CONFIG_DEVMODE // now kill the selected voice itSelectedVoice->Kill(itNoteOnEvent); @@ -796,8 +865,13 @@ itLastStolenVoice = itSelectedVoice; --VoiceTheftsLeft; + + return 0; // success + } + else { + dmsg(1,("Event pool emtpy!\n")); + return -1; } - else dmsg(1,("Event pool emtpy!\n")); } /** @@ -856,67 +930,81 @@ void Engine::ProcessControlChange(EngineChannel* pEngineChannel, Pool::Iterator& itControlChangeEvent) { dmsg(4,("Engine::ContinuousController cc=%d v=%d\n", itControlChangeEvent->Param.CC.Controller, itControlChangeEvent->Param.CC.Value)); - switch (itControlChangeEvent->Param.CC.Controller) { + // update controller value in the engine channel's controller table + pEngineChannel->ControllerTable[itControlChangeEvent->Param.CC.Controller] = itControlChangeEvent->Param.CC.Value; + + // move event from the unsorted event list to the control change event list + Pool::Iterator itControlChangeEventOnCCList = itControlChangeEvent.moveToEndOf(pEngineChannel->pCCEvents); + + switch (itControlChangeEventOnCCList->Param.CC.Controller) { case 7: { // volume //TODO: not sample accurate yet - pEngineChannel->GlobalVolume = (float) itControlChangeEvent->Param.CC.Value / 127.0f; + pEngineChannel->GlobalVolume = (float) itControlChangeEventOnCCList->Param.CC.Value / 127.0f; break; } case 10: { // panpot //TODO: not sample accurate yet - const int pan = (int) itControlChangeEvent->Param.CC.Value - 64; + const int pan = (int) itControlChangeEventOnCCList->Param.CC.Value - 64; pEngineChannel->GlobalPanLeft = 1.0f - float(RTMath::Max(pan, 0)) / 63.0f; pEngineChannel->GlobalPanRight = 1.0f - float(RTMath::Min(pan, 0)) / -64.0f; break; } case 64: { // sustain - if (itControlChangeEvent->Param.CC.Value >= 64 && !pEngineChannel->SustainPedal) { + if (itControlChangeEventOnCCList->Param.CC.Value >= 64 && !pEngineChannel->SustainPedal) { dmsg(4,("PEDAL DOWN\n")); pEngineChannel->SustainPedal = true; // cancel release process of voices if necessary RTList::Iterator iuiKey = pEngineChannel->pActiveKeys->first(); - if (iuiKey) { - itControlChangeEvent->Type = Event::type_cancel_release; // transform event type - while (iuiKey) { - midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[*iuiKey]; - ++iuiKey; - if (!pKey->KeyPressed) { - RTList::Iterator itNewEvent = pKey->pEvents->allocAppend(); - if (itNewEvent) *itNewEvent = *itControlChangeEvent; // copy event to the key's own event list - else dmsg(1,("Event pool emtpy!\n")); + for (; iuiKey; ++iuiKey) { + midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[*iuiKey]; + if (!pKey->KeyPressed) { + RTList::Iterator itNewEvent = pKey->pEvents->allocAppend(); + if (itNewEvent) { + *itNewEvent = *itControlChangeEventOnCCList; // copy event to the key's own event list + itNewEvent->Type = Event::type_cancel_release; // transform event type } + else dmsg(1,("Event pool emtpy!\n")); } } } - if (itControlChangeEvent->Param.CC.Value < 64 && pEngineChannel->SustainPedal) { + if (itControlChangeEventOnCCList->Param.CC.Value < 64 && pEngineChannel->SustainPedal) { dmsg(4,("PEDAL UP\n")); pEngineChannel->SustainPedal = false; // release voices if their respective key is not pressed RTList::Iterator iuiKey = pEngineChannel->pActiveKeys->first(); - if (iuiKey) { - itControlChangeEvent->Type = Event::type_release; // transform event type - while (iuiKey) { - midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[*iuiKey]; - ++iuiKey; - if (!pKey->KeyPressed) { - RTList::Iterator itNewEvent = pKey->pEvents->allocAppend(); - if (itNewEvent) *itNewEvent = *itControlChangeEvent; // copy event to the key's own event list - else dmsg(1,("Event pool emtpy!\n")); + for (; iuiKey; ++iuiKey) { + midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[*iuiKey]; + if (!pKey->KeyPressed) { + RTList::Iterator itNewEvent = pKey->pEvents->allocAppend(); + if (itNewEvent) { + *itNewEvent = *itControlChangeEventOnCCList; // copy event to the key's own event list + itNewEvent->Type = Event::type_release; // transform event type } + else dmsg(1,("Event pool emtpy!\n")); } } } break; } - } - // update controller value in the engine's controller table - pEngineChannel->ControllerTable[itControlChangeEvent->Param.CC.Controller] = itControlChangeEvent->Param.CC.Value; - // move event from the unsorted event list to the control change event list - itControlChangeEvent.moveToEndOf(pEngineChannel->pCCEvents); + // Channel Mode Messages + + case 120: { // all sound off + KillAllVoices(pEngineChannel, itControlChangeEventOnCCList); + break; + } + case 121: { // reset all controllers + pEngineChannel->ResetControllers(); + break; + } + case 123: { // all notes off + ReleaseAllVoices(pEngineChannel, itControlChangeEventOnCCList); + break; + } + } } /** @@ -934,6 +1022,7 @@ switch (id) { case 0x41: { // Roland + dmsg(3,("Roland Sysex\n")); uint8_t device_id, model_id, cmd_id; if (!reader.pop(&device_id)) goto free_sysex_data; if (!reader.pop(&model_id)) goto free_sysex_data; @@ -946,19 +1035,26 @@ const RingBuffer::NonVolatileReader checksum_reader = reader; // so we can calculate the check sum later if (reader.read(&addr[0], 3) != 3) goto free_sysex_data; if (addr[0] == 0x40 && addr[1] == 0x00) { // System Parameters + dmsg(3,("\tSystem Parameter\n")); } else if (addr[0] == 0x40 && addr[1] == 0x01) { // Common Parameters + dmsg(3,("\tCommon Parameter\n")); } else if (addr[0] == 0x40 && (addr[1] & 0xf0) == 0x10) { // Part Parameters (1) - switch (addr[3]) { + dmsg(3,("\tPart Parameter\n")); + switch (addr[2]) { case 0x40: { // scale tuning + dmsg(3,("\t\tScale Tuning\n")); uint8_t scale_tunes[12]; // detuning of all 12 semitones of an octave if (reader.read(&scale_tunes[0], 12) != 12) goto free_sysex_data; uint8_t checksum; - if (!reader.pop(&checksum)) goto free_sysex_data; - if (GSCheckSum(checksum_reader, 12) != checksum) goto free_sysex_data; + if (!reader.pop(&checksum)) goto free_sysex_data; + #if CONFIG_ASSERT_GS_SYSEX_CHECKSUM + if (GSCheckSum(checksum_reader, 12)) goto free_sysex_data; + #endif // CONFIG_ASSERT_GS_SYSEX_CHECKSUM for (int i = 0; i < 12; i++) scale_tunes[i] -= 64; AdjustScale((int8_t*) scale_tunes); + dmsg(3,("\t\t\tNew scale applied.\n")); break; } } @@ -1003,6 +1099,51 @@ } /** + * Releases all voices on an engine channel. All voices will go into + * the release stage and thus it might take some time (e.g. dependant to + * their envelope release time) until they actually die. + * + * @param pEngineChannel - engine channel on which all voices should be released + * @param itReleaseEvent - event which caused this releasing of all voices + */ + void Engine::ReleaseAllVoices(EngineChannel* pEngineChannel, Pool::Iterator& itReleaseEvent) { + RTList::Iterator iuiKey = pEngineChannel->pActiveKeys->first(); + while (iuiKey) { + midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[*iuiKey]; + ++iuiKey; + // append a 'release' event to the key's own event list + 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 + } + else dmsg(1,("Event pool emtpy!\n")); + } + } + + /** + * Kills all voices on an engine channel as soon as possible. Voices + * won't get into release state, their volume level will be ramped down + * as fast as possible. + * + * @param pEngineChannel - engine channel on which all voices should be killed + * @param itKillEvent - event which caused this killing of all voices + */ + void Engine::KillAllVoices(EngineChannel* pEngineChannel, Pool::Iterator& itKillEvent) { + RTList::Iterator iuiKey = pEngineChannel->pActiveKeys->first(); + RTList::Iterator end = pEngineChannel->pActiveKeys->end(); + while (iuiKey != end) { // iterate through all active keys + midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[*iuiKey]; + ++iuiKey; + RTList::Iterator itVoice = pKey->pActiveVoices->first(); + RTList::Iterator itVoicesEnd = pKey->pActiveVoices->end(); + for (; itVoice != itVoicesEnd; ++itVoice) { // iterate through all voices on this key + itVoice->Kill(itKillEvent); + } + } + } + + /** * Initialize the parameter sequence for the modulation destination given by * by 'dst' with the constant value given by val. */ @@ -1046,7 +1187,7 @@ } String Engine::EngineName() { - return "GigEngine"; + return LS_GIG_ENGINE_NAME; } String Engine::Description() { @@ -1054,7 +1195,7 @@ } String Engine::Version() { - String s = "$Revision: 1.32 $"; + String s = "$Revision: 1.39 $"; return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword }