/[svn]/linuxsampler/trunk/src/engines/gig/Engine.cpp
ViewVC logotype

Diff of /linuxsampler/trunk/src/engines/gig/Engine.cpp

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 2011 by persson, Thu Jul 9 17:37:41 2009 UTC revision 2012 by iliev, Fri Oct 23 17:53:17 2009 UTC
# Line 4  Line 4 
4   *                                                                         *   *                                                                         *
5   *   Copyright (C) 2003,2004 by Benno Senoner and Christian Schoenebeck    *   *   Copyright (C) 2003,2004 by Benno Senoner and Christian Schoenebeck    *
6   *   Copyright (C) 2005-2009 Christian Schoenebeck                         *   *   Copyright (C) 2005-2009 Christian Schoenebeck                         *
7     *   Copyright (C) 2009 Grigor Iliev                                       *
8   *                                                                         *   *                                                                         *
9   *   This program is free software; you can redistribute it and/or modify  *   *   This program is free software; you can redistribute it and/or modify  *
10   *   it under the terms of the GNU General Public License as published by  *   *   it under the terms of the GNU General Public License as published by  *
# Line 21  Line 22 
22   *   MA  02111-1307  USA                                                   *   *   MA  02111-1307  USA                                                   *
23   ***************************************************************************/   ***************************************************************************/
24    
 #include <sstream>  
 #include "DiskThread.h"  
 #include "Voice.h"  
 #include "EGADSR.h"  
 #include "../EngineFactory.h"  
   
25  #include "Engine.h"  #include "Engine.h"
26    #include "EngineChannel.h"
 #include "../../common/global_private.h"  
27    
28  namespace LinuxSampler { namespace gig {  namespace LinuxSampler { namespace gig {
29        Engine::Format Engine::GetEngineFormat() { return GIG; }
     InstrumentResourceManager Engine::instruments;  
   
     std::map<AudioOutputDevice*,Engine*> Engine::engines;  
   
     /**  
      * Get a gig::Engine object for the given gig::EngineChannel and the  
      * given AudioOutputDevice. All engine channels which are connected to  
      * the same audio output device will use the same engine instance. This  
      * method will be called by a gig::EngineChannel whenever it's  
      * connecting to a audio output device.  
      *  
      * @param pChannel - engine channel which acquires an engine object  
      * @param pDevice  - the audio output device \a pChannel is connected to  
      */  
     Engine* Engine::AcquireEngine(LinuxSampler::gig::EngineChannel* pChannel, AudioOutputDevice* pDevice) {  
         Engine* pEngine = NULL;  
         // check if there's already an engine for the given audio output device  
         if (engines.count(pDevice)) {  
             dmsg(4,("Using existing gig::Engine.\n"));  
             pEngine = engines[pDevice];  
   
             // Disable the engine while the new engine channel is  
             // added and initialized. The engine will be enabled again  
             // in EngineChannel::Connect.  
             pEngine->DisableAndLock();  
         } else { // create a new engine (and disk thread) instance for the given audio output device  
             dmsg(4,("Creating new gig::Engine.\n"));  
             pEngine = (Engine*) EngineFactory::Create("gig");  
             pEngine->Connect(pDevice);  
             engines[pDevice] = pEngine;  
         }  
         // register engine channel to the engine instance  
         pEngine->engineChannels.add(pChannel);  
         // remember index in the ArrayList  
         pChannel->iEngineIndexSelf = pEngine->engineChannels.size() - 1;  
         dmsg(4,("This gig::Engine has now %d EngineChannels.\n",pEngine->engineChannels.size()));  
         return pEngine;  
     }  
   
     /**  
      * Once an engine channel is disconnected from an audio output device,  
      * it will immediately call this method to unregister itself from the  
      * engine instance and if that engine instance is not used by any other  
      * engine channel anymore, then that engine instance will be destroyed.  
      *  
      * @param pChannel - engine channel which wants to disconnect from it's  
      *                   engine instance  
      * @param pDevice  - audio output device \a pChannel was connected to  
      */  
     void Engine::FreeEngine(LinuxSampler::gig::EngineChannel* pChannel, AudioOutputDevice* pDevice) {  
         dmsg(4,("Disconnecting EngineChannel from gig::Engine.\n"));  
         Engine* pEngine = engines[pDevice];  
         // unregister EngineChannel from the Engine instance  
         pEngine->engineChannels.remove(pChannel);  
         // if the used Engine instance is not used anymore, then destroy it  
         if (pEngine->engineChannels.empty()) {  
             pDevice->Disconnect(pEngine);  
             engines.erase(pDevice);  
             delete pEngine;  
             dmsg(4,("Destroying gig::Engine.\n"));  
         }  
         else dmsg(4,("This gig::Engine has now %d EngineChannels.\n",pEngine->engineChannels.size()));  
     }  
   
     /**  
      * Constructor  
      */  
     Engine::Engine() : SuspendedRegions(128) {  
         pAudioOutputDevice = NULL;  
         pDiskThread        = NULL;  
         pEventGenerator    = NULL;  
         pSysexBuffer       = new RingBuffer<uint8_t,false>(CONFIG_SYSEX_BUFFER_SIZE, 0);  
         pEventQueue        = new RingBuffer<Event,false>(CONFIG_MAX_EVENTS_PER_FRAGMENT, 0);  
         pEventPool         = new Pool<Event>(CONFIG_MAX_EVENTS_PER_FRAGMENT);  
         pVoicePool         = new Pool<Voice>(GLOBAL_MAX_VOICES);  
         pDimRegionPool[0]  = new Pool< ::gig::DimensionRegion*>(GLOBAL_MAX_VOICES);  
         pDimRegionPool[1]  = new Pool< ::gig::DimensionRegion*>(GLOBAL_MAX_VOICES);  
         pVoiceStealingQueue = new RTList<Event>(pEventPool);  
         pGlobalEvents      = new RTList<Event>(pEventPool);  
         iMaxDiskStreams    = GLOBAL_MAX_STREAMS;  
         FrameTime          = 0;  
   
         for (RTList<Voice>::Iterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) {  
             iterVoice->SetEngine(this);  
         }  
         pVoicePool->clear();  
   
         ResetInternal();  
         ResetScaleTuning();  
         ResetSuspendedRegions();  
     }  
   
     /**  
      * Destructor  
      */  
     Engine::~Engine() {  
         MidiInputPort::RemoveSysexListener(this);  
         if (pDiskThread) {  
             dmsg(1,("Stopping disk thread..."));  
             pDiskThread->StopThread();  
             delete pDiskThread;  
             dmsg(1,("OK\n"));  
         }  
         if (pEventQueue) delete pEventQueue;  
         if (pEventPool)  delete pEventPool;  
         if (pVoicePool) {  
             pVoicePool->clear();  
             delete pVoicePool;  
         }  
         if (pEventGenerator) delete pEventGenerator;  
         if (pVoiceStealingQueue) delete pVoiceStealingQueue;  
         if (pSysexBuffer) delete pSysexBuffer;  
         if (pGlobalEvents) delete pGlobalEvents;  
         if (pDimRegionPool[0]) delete pDimRegionPool[0];  
         if (pDimRegionPool[1]) delete pDimRegionPool[1];  
         ResetSuspendedRegions();  
         Unregister();  
     }  
   
     void Engine::Enable() {  
         dmsg(3,("gig::Engine: enabling\n"));  
         EngineDisabled.PushAndUnlock(false, 2, 0, true); // set condition object 'EngineDisabled' to false (wait max. 2s)  
         dmsg(3,("gig::Engine: enabled (val=%d)\n", EngineDisabled.GetUnsafe()));  
     }  
   
     /**  
      * Temporarily stop the engine to not do anything. The engine will just be  
      * frozen during that time, that means after enabling it again it will  
      * continue where it was, with all its voices and playback state it had at  
      * the point of disabling. Notice that the engine's (audio) thread will  
      * continue to run, it just remains in an inactive loop during that time.  
      *  
      * If you need to be sure that all voices and disk streams are killed as  
      * well, use @c SuspendAll() instead.  
      *  
      * @see Enable(), SuspendAll()  
      */  
     void Engine::Disable() {  
         dmsg(3,("gig::Engine: disabling\n"));  
         bool* pWasDisabled = EngineDisabled.PushAndUnlock(true, 2); // wait max. 2s  
         if (!pWasDisabled) dmsg(3,("gig::Engine warning: Timeout waiting to disable engine.\n"));  
     }  
   
     void Engine::DisableAndLock() {  
         dmsg(3,("gig::Engine: disabling\n"));  
         bool* pWasDisabled = EngineDisabled.Push(true, 2); // wait max. 2s  
         if (!pWasDisabled) dmsg(3,("gig::Engine warning: Timeout waiting to disable engine.\n"));  
     }  
30    
31      /**      /**
32       * Similar to @c Disable() but this method additionally kills all voices       *  Reacts on supported control change commands (e.g. pitch bend wheel,
33       * and disk streams and blocks until all voices and disk streams are actually       *  modulation wheel, aftertouch).
      * killed / deleted.  
      *  
      * @e Note: only the original calling thread is able to re-enable the  
      * engine afterwards by calling @c ResumeAll() later on!  
      */  
     void Engine::SuspendAll() {  
         dmsg(2,("gig::Engine: Suspending all ...\n"));  
         // stop the engine, so we can safely modify the engine's  
         // data structures from this foreign thread  
         DisableAndLock();  
         // we could also use the respective class member variable here,  
         // but this is probably safer and cleaner  
         int iPendingStreamDeletions = 0;  
         // kill all voices on all engine channels the *die hard* way  
         for (int iChannel = 0; iChannel < engineChannels.size(); iChannel++) {  
             EngineChannel* pEngineChannel = engineChannels[iChannel];  
             RTList<uint>::Iterator iuiKey = pEngineChannel->pActiveKeys->first();  
             RTList<uint>::Iterator end    = pEngineChannel->pActiveKeys->end();  
             for (; iuiKey != end; ++iuiKey) { // iterate through all active keys  
                 midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[*iuiKey];  
                 RTList<Voice>::Iterator itVoice = pKey->pActiveVoices->first();  
                 RTList<Voice>::Iterator 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++;  
                     }  
                     // free the voice to the voice pool and update key info  
                     FreeVoice(pEngineChannel, itVoice);  
                 }  
             }  
         }  
         // wait until all streams were actually deleted by the disk thread  
         while (iPendingStreamDeletions) {  
             while (  
                 iPendingStreamDeletions &&  
                 pDiskThread->AskForDeletedStream() != Stream::INVALID_HANDLE  
             ) iPendingStreamDeletions--;  
             if (!iPendingStreamDeletions) break;  
             usleep(10000); // sleep for 10ms  
         }  
         dmsg(2,("gig::Engine: Everything suspended.\n"));  
     }  
   
     /**  
      * At the moment same as calling @c Enable() directly, but this might  
      * change in future, so better call this method as counterpart to  
      * @c SuspendAll() instead of @c Enable() !  
      */  
     void Engine::ResumeAll() {  
         Enable();  
     }  
   
     /**  
      * Order the engine to stop rendering audio for the given region.  
      * Additionally this method will block until all voices and their disk  
      * streams associated with that region are actually killed / deleted, so  
      * one can i.e. safely modify the region with an instrument editor after  
      * returning from this method.  
      *  
      * @param pRegion - region the engine shall stop using  
      */  
     void Engine::Suspend(::gig::Region* pRegion) {  
         dmsg(2,("gig::Engine: Suspending Region %x ...\n",pRegion));  
         SuspendedRegionsMutex.Lock();  
         SuspensionChangeOngoing.Set(true);  
         pPendingRegionSuspension = pRegion;  
         SuspensionChangeOngoing.WaitAndUnlockIf(true);  
         SuspendedRegionsMutex.Unlock();  
         dmsg(2,("gig::Engine: Region %x suspended.",pRegion));  
     }  
   
     /**  
      * Orders the engine to resume playing back the given region, previously  
      * suspended with @c Suspend() .  
      *  
      * @param pRegion - region the engine shall be allowed to use again  
      */  
     void Engine::Resume(::gig::Region* pRegion) {  
         dmsg(2,("gig::Engine: Resuming Region %x ...\n",pRegion));  
         SuspendedRegionsMutex.Lock();  
         SuspensionChangeOngoing.Set(true);  
         pPendingRegionResumption = pRegion;  
         SuspensionChangeOngoing.WaitAndUnlockIf(true);  
         SuspendedRegionsMutex.Unlock();  
         dmsg(2,("gig::Engine: Region %x resumed.\n",pRegion));  
     }  
   
     /**  
      *  Reset all voices and disk thread and clear input event queue and all  
      *  control and status variables.  
      */  
     void Engine::Reset() {  
         DisableAndLock();  
         ResetInternal();  
         ResetScaleTuning();  
         Enable();  
     }  
   
     /**  
      *  Reset all voices and disk thread and clear input event queue and all  
      *  control and status variables. This method is protected by a mutex.  
      */  
     void Engine::ResetInternal() {  
         ResetInternalMutex.Lock();  
   
         // make sure that the engine does not get any sysex messages  
         // while it's reseting  
         bool sysexDisabled = MidiInputPort::RemoveSysexListener(this);  
         SetVoiceCount(0);  
         ActiveVoiceCountMax = 0;  
   
         // reset voice stealing parameters  
         pVoiceStealingQueue->clear();  
         itLastStolenVoice          = RTList<Voice>::Iterator();  
         itLastStolenVoiceGlobally  = RTList<Voice>::Iterator();  
         iuiLastStolenKey           = RTList<uint>::Iterator();  
         iuiLastStolenKeyGlobally   = RTList<uint>::Iterator();  
         pLastStolenChannel         = NULL;  
   
         // reset all voices  
         for (RTList<Voice>::Iterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) {  
             iterVoice->Reset();  
         }  
         pVoicePool->clear();  
   
         // reset disk thread  
         if (pDiskThread) pDiskThread->Reset();  
   
         // delete all input events  
         pEventQueue->init();  
         pSysexBuffer->init();  
         if (sysexDisabled) MidiInputPort::AddSysexListener(this);  
         ResetInternalMutex.Unlock();  
     }  
   
     /**  
      * Reset to normal, chromatic scale (means equal tempered).  
      */  
     void Engine::ResetScaleTuning() {  
         memset(&ScaleTuning[0], 0x00, 12);  
     }  
   
     void Engine::ResetSuspendedRegions() {  
         SuspendedRegions.clear();  
         iPendingStreamDeletions = 0;  
         pPendingRegionSuspension = pPendingRegionResumption = NULL;  
         SuspensionChangeOngoing.Set(false);  
     }  
   
     /**  
      * 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) {  
         // caution: don't ignore if connecting to the same device here,  
         // because otherwise SetMaxDiskStreams() implementation won't work anymore!  
   
         pAudioOutputDevice = pAudioOut;  
   
         ResetInternal();  
   
         // inform audio driver for the need of two channels  
         try {  
             pAudioOutputDevice->AcquireChannels(2); // gig engine only stereo  
         }  
         catch (AudioOutputException e) {  
             String msg = "Audio output device unable to provide 2 audio channels, cause: " + e.Message();  
             throw Exception(msg);  
         }  
   
         this->MaxSamplesPerCycle = pAudioOutputDevice->MaxSamplesPerCycle();  
         this->SampleRate         = pAudioOutputDevice->SampleRate();  
   
         MinFadeOutSamples = int(double(SampleRate) * CONFIG_EG_MIN_RELEASE_TIME) - 1;  
         if (MaxSamplesPerCycle < MinFadeOutSamples) {  
             std::cerr << "gig::Engine: WARNING, CONFIG_EG_MIN_RELEASE_TIME "  
                       << "too big for current audio fragment size & sampling rate! "  
                       << "May lead to click sounds if voice stealing chimes in!\n" << std::flush;  
             // force volume ramp downs at the beginning of each fragment  
             MinFadeOutSamples = MaxSamplesPerCycle;  
             // lower minimum release time  
             const float minReleaseTime = (float) MaxSamplesPerCycle / (float) SampleRate;  
             for (RTList<Voice>::Iterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) {  
                 iterVoice->EG1.CalculateFadeOutCoeff(minReleaseTime, SampleRate);  
             }  
             pVoicePool->clear();  
         }  
   
         // (re)create disk thread  
         if (this->pDiskThread) {  
             dmsg(1,("Stopping disk thread..."));  
             this->pDiskThread->StopThread();  
             delete this->pDiskThread;  
             dmsg(1,("OK\n"));  
         }  
         this->pDiskThread =  
             new DiskThread(  
                 iMaxDiskStreams,  
                 ((pAudioOut->MaxSamplesPerCycle() << CONFIG_MAX_PITCH) << 1) + 6, //FIXME: assuming stereo  
                 &instruments  
             );  
   
         if (!pDiskThread) {  
             dmsg(0,("gig::Engine  new diskthread = NULL\n"));  
             exit(EXIT_FAILURE);  
         }  
   
         for (RTList<Voice>::Iterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) {  
             iterVoice->pDiskThread = this->pDiskThread;  
             dmsg(3,("d"));  
         }  
         pVoicePool->clear();  
   
         // (re)create event generator  
         if (pEventGenerator) delete pEventGenerator;  
         pEventGenerator = new EventGenerator(pAudioOut->SampleRate());  
   
         dmsg(1,("Starting disk thread..."));  
         pDiskThread->StartThread();  
         dmsg(1,("OK\n"));  
   
         for (RTList<Voice>::Iterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) {  
             if (!iterVoice->pDiskThread) {  
                 dmsg(0,("Engine -> voice::trigger: !pDiskThread\n"));  
                 exit(EXIT_FAILURE);  
             }  
         }  
         pVoicePool->clear();  
     }  
   
     /**  
      * Called by the engine's (audio) thread once per cycle to process requests  
      * from the outer world to suspend or resume a given @c gig::Region .  
      */  
     void Engine::ProcessSuspensionsChanges() {  
         // process request for suspending one region  
         if (pPendingRegionSuspension) {  
             // kill all voices on all engine channels that use this region  
             for (int iChannel = 0; iChannel < engineChannels.size(); iChannel++) {  
                 EngineChannel* pEngineChannel = engineChannels[iChannel];  
                 RTList<uint>::Iterator iuiKey = pEngineChannel->pActiveKeys->first();  
                 RTList<uint>::Iterator end    = pEngineChannel->pActiveKeys->end();  
                 for (; iuiKey != end; ++iuiKey) { // iterate through all active keys  
                     midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[*iuiKey];  
                     RTList<Voice>::Iterator itVoice = pKey->pActiveVoices->first();  
                     // if current key is not associated with this region, skip this key  
                     if (itVoice->pDimRgn->GetParent() != pPendingRegionSuspension) continue;  
                     RTList<Voice>::Iterator 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++;  
                         }  
                         //NOTE: maybe we should call FreeVoice() here, shouldn't cause a harm though I think, since the voices should be freed by RenderActiveVoices() in the render loop, they are probably just freed a bit later than they could/should be  
                     }  
                 }  
             }  
             // make sure the region is not yet on the list  
             bool bAlreadySuspended = false;  
             RTList< ::gig::Region*>::Iterator iter = SuspendedRegions.first();  
             RTList< ::gig::Region*>::Iterator end  = SuspendedRegions.end();  
             for (; iter != end; ++iter) { // iterate through all suspended regions  
                 if (*iter == pPendingRegionSuspension) { // found  
                     bAlreadySuspended = true;  
                     dmsg(1,("gig::Engine: attempt to suspend an already suspended region !!!\n"));  
                     break;  
                 }  
             }  
             if (!bAlreadySuspended) {  
                 // put the region on the list of suspended regions  
                 RTList< ::gig::Region*>::Iterator iter = SuspendedRegions.allocAppend();  
                 if (iter) {  
                     *iter = pPendingRegionSuspension;  
                 } else std::cerr << "gig::Engine: Could not suspend Region, list is full. This is a bug!!!\n" << std::flush;  
             }  
             // free request slot for next caller (and to make sure that  
             // we're not going to process the same request in the next cycle)  
             pPendingRegionSuspension = NULL;  
             // if no disk stream deletions are pending, awaken other side, as  
             // we're done in this case  
             if (!iPendingStreamDeletions) SuspensionChangeOngoing.Set(false);  
         }  
   
         // process request for resuming one region  
         if (pPendingRegionResumption) {  
             // remove region from the list of suspended regions  
             RTList< ::gig::Region*>::Iterator iter = SuspendedRegions.first();  
             RTList< ::gig::Region*>::Iterator end  = SuspendedRegions.end();  
             for (; iter != end; ++iter) { // iterate through all suspended regions  
                 if (*iter == pPendingRegionResumption) { // found  
                     SuspendedRegions.free(iter);  
                     break; // done  
                 }  
             }  
             // free request slot for next caller  
             pPendingRegionResumption = NULL;  
             // awake other side as we're done  
             SuspensionChangeOngoing.Set(false);  
         }  
     }  
   
     /**  
      * Called by the engine's (audio) thread once per cycle to check if  
      * streams of voices that were killed due to suspension request have  
      * finally really been deleted by the disk thread.  
      */  
     void Engine::ProcessPendingStreamDeletions() {  
         if (!iPendingStreamDeletions) return;  
         //TODO: or shall we better store a list with stream handles instead of a scalar amount of streams to be deleted? might be safer  
         while (  
             iPendingStreamDeletions &&  
             pDiskThread->AskForDeletedStream() != Stream::INVALID_HANDLE  
         ) iPendingStreamDeletions--;  
         // just for safety ...  
         while (pDiskThread->AskForDeletedStream() != Stream::INVALID_HANDLE);  
         // now that all disk streams are deleted, awake other side as  
         // we're finally done with suspending the requested region  
         if (!iPendingStreamDeletions) SuspensionChangeOngoing.Set(false);  
     }  
   
     /**  
      * Returns @c true if the given region is currently set to be suspended  
      * from being used, @c false otherwise.  
      */  
     bool Engine::RegionSuspended(::gig::Region* pRegion) {  
         if (SuspendedRegions.isEmpty()) return false;  
         //TODO: or shall we use a sorted container instead of the RTList? might be faster ... or trivial ;-)  
         RTList< ::gig::Region*>::Iterator iter = SuspendedRegions.first();  
         RTList< ::gig::Region*>::Iterator end  = SuspendedRegions.end();  
         for (; iter != end; ++iter)  // iterate through all suspended regions  
             if (*iter == pRegion) return true;  
         return false;  
     }  
   
     /**  
      * Clear all engine global event lists.  
      */  
     void Engine::ClearEventLists() {  
         pGlobalEvents->clear();  
     }  
   
     /**  
      * Copy all events from the engine's global input queue buffer to the  
      * engine's internal event list. This will be done at the beginning of  
      * each audio cycle (that is each RenderAudio() call) to distinguish  
      * all global events which have to be processed in the current audio  
      * cycle. These events are usually just SysEx messages. Every  
      * EngineChannel has it's own input event queue buffer and event list  
      * to handle common events like NoteOn, NoteOff and ControlChange  
      * events.  
      *  
      * @param Samples - number of sample points to be processed in the  
      *                  current audio cycle  
      */  
     void Engine::ImportEvents(uint Samples) {  
         RingBuffer<Event,false>::NonVolatileReader eventQueueReader = pEventQueue->get_non_volatile_reader();  
         Event* pEvent;  
         while (true) {  
             // get next event from input event queue  
             if (!(pEvent = eventQueueReader.pop())) break;  
             // if younger event reached, ignore that and all subsequent ones for now  
             if (pEvent->FragmentPos() >= Samples) {  
                 eventQueueReader--;  
                 dmsg(2,("Younger Event, pos=%d ,Samples=%d!\n",pEvent->FragmentPos(),Samples));  
                 pEvent->ResetFragmentPos();  
                 break;  
             }  
             // copy event to internal event list  
             if (pGlobalEvents->poolIsEmpty()) {  
                 dmsg(1,("Event pool emtpy!\n"));  
                 break;  
             }  
             *pGlobalEvents->allocAppend() = *pEvent;  
         }  
         eventQueueReader.free(); // free all copied events from input queue  
     }  
   
     /**  
      * Let this engine proceed to render the given amount of sample points.  
      * The engine will iterate through all engine channels and render audio  
      * for each engine channel independently. The calculated audio data of  
      * all voices of each engine channel will be placed into the audio sum  
      * buffers of the respective audio output device, connected to the  
      * respective engine channel.  
34       *       *
35       *  @param Samples - number of sample points to be rendered       *  @param pEngineChannel - engine channel on which this event occured on
36       *  @returns       0 on success       *  @param itControlChangeEvent - controller, value and time stamp of the event
37       */       */
38      int Engine::RenderAudio(uint Samples) {      void Engine::ProcessControlChange (
39          dmsg(8,("RenderAudio(Samples=%d)\n", Samples));          LinuxSampler::EngineChannel*  pEngineChannel,
40            Pool<Event>::Iterator&        itControlChangeEvent
41          // return if engine disabled      ) {
42          if (EngineDisabled.Pop()) {          dmsg(4,("Engine::ContinuousController cc=%d v=%d\n", itControlChangeEvent->Param.CC.Controller, itControlChangeEvent->Param.CC.Value));
             dmsg(5,("gig::Engine: engine disabled (val=%d)\n",EngineDisabled.GetUnsafe()));  
             EngineDisabled.RttDone();  
             return 0;  
         }  
   
         // process requests for suspending / resuming regions (i.e. to avoid  
         // crashes while these regions are modified by an instrument editor)  
         ProcessSuspensionsChanges();  
   
         // update time of start and end of this audio fragment (as events' time stamps relate to this)  
         pEventGenerator->UpdateFragmentTime(Samples);  
   
         // We only allow the given maximum number of voices to be spawned  
         // in each audio fragment. All subsequent request for spawning new  
         // voices in the same audio fragment will be ignored.  
         VoiceSpawnsLeft = MaxVoices();  
   
         // get all events from the engine's global input event queue which belong to the current fragment  
         // (these are usually just SysEx messages)  
         ImportEvents(Samples);  
   
         // process engine global events (these are currently only MIDI System Exclusive messages)  
         {  
             RTList<Event>::Iterator itEvent = pGlobalEvents->first();  
             RTList<Event>::Iterator end     = pGlobalEvents->end();  
             for (; itEvent != end; ++itEvent) {  
                 switch (itEvent->Type) {  
                     case Event::type_sysex:  
                         dmsg(5,("Engine: Sysex received\n"));  
                         ProcessSysex(itEvent);  
                         break;  
                 }  
             }  
         }  
   
         // reset internal voice counter (just for statistic of active voices)  
         ActiveVoiceCountTemp = 0;  
   
         // handle instrument change commands  
         bool instrumentChanged = false;  
         for (int i = 0; i < engineChannels.size(); i++) {  
             EngineChannel* pEngineChannel = engineChannels[i];  
   
             // as we're going to (carefully) write some status to the  
             // synchronized struct, we cast away the const  
             EngineChannel::instrument_change_command_t& cmd =  
                 const_cast<EngineChannel::instrument_change_command_t&>(pEngineChannel->InstrumentChangeCommandReader.Lock());  
   
             pEngineChannel->pDimRegionsInUse = cmd.pDimRegionsInUse;  
             pEngineChannel->pDimRegionsInUse->clear();  
   
             if (cmd.bChangeInstrument) {  
                 // change instrument  
                 dmsg(5,("Engine: instrument change command received\n"));  
                 cmd.bChangeInstrument = false;  
                 pEngineChannel->pInstrument = cmd.pInstrument;  
                 instrumentChanged = true;  
   
                 // Iterate through all active voices and mark them as  
                 // "orphans", which means that the dimension regions  
                 // and samples they use should be released to the  
                 // instrument resource manager when the voices die.  
                 int i = 0;  
                 RTList<uint>::Iterator iuiKey = pEngineChannel->pActiveKeys->first();  
                 RTList<uint>::Iterator end    = pEngineChannel->pActiveKeys->end();  
                 while (iuiKey != end) { // iterate through all active keys  
                     midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[*iuiKey];  
                     ++iuiKey;  
   
                     RTList<Voice>::Iterator itVoice     = pKey->pActiveVoices->first();  
                     RTList<Voice>::Iterator itVoicesEnd = pKey->pActiveVoices->end();  
                     for (; itVoice != itVoicesEnd; ++itVoice) { // iterate through all voices on this key  
                         itVoice->Orphan = true;  
                     }  
                 }  
             }  
         }  
         if (instrumentChanged) {  
             //TODO: this is a lazy solution ATM and not safe in case somebody is currently editing the instrument we're currently switching to (we should store all suspended regions on instrument manager side and when switching to another instrument copy that list to the engine's local list of suspensions  
             ResetSuspendedRegions();  
         }  
   
         // handle events on all engine channels  
         for (int i = 0; i < engineChannels.size(); i++) {  
             ProcessEvents(engineChannels[i], Samples);  
         }  
   
         // render all 'normal', active voices on all engine channels  
         for (int i = 0; i < engineChannels.size(); i++) {  
             RenderActiveVoices(engineChannels[i], Samples);  
         }  
   
         // now that all ordinary voices on ALL engine channels are rendered, render new stolen voices  
         RenderStolenVoices(Samples);  
   
         // handle audio routing for engine channels with FX sends  
         for (int i = 0; i < engineChannels.size(); i++) {  
             if (engineChannels[i]->fxSends.empty()) continue; // ignore if no FX sends  
             RouteAudio(engineChannels[i], Samples);  
         }  
   
         // handle cleanup on all engine channels for the next audio fragment  
         for (int i = 0; i < engineChannels.size(); i++) {  
             PostProcess(engineChannels[i]);  
         }  
   
   
         // empty the engine's event list for the next audio fragment  
         ClearEventLists();  
   
         // reset voice stealing for the next audio fragment  
         pVoiceStealingQueue->clear();  
   
         // just some statistics about this engine instance  
         SetVoiceCount(ActiveVoiceCountTemp);  
         if (VoiceCount() > ActiveVoiceCountMax) ActiveVoiceCountMax = VoiceCount();  
   
         // in case regions were previously suspended and we killed voices  
         // with disk streams due to that, check if those streams have finally  
         // been deleted by the disk thread  
         if (iPendingStreamDeletions) ProcessPendingStreamDeletions();  
   
         for (int i = 0; i < engineChannels.size(); i++) {  
             engineChannels[i]->InstrumentChangeCommandReader.Unlock();  
         }  
         FrameTime += Samples;  
43    
44          EngineDisabled.RttDone();          EngineChannel* pChannel = dynamic_cast<EngineChannel*>(pEngineChannel);
45          return 0;          // handle the "control triggered" MIDI rule: a control change
46      }          // event can trigger a new note on or note off event
47            if (pChannel->pInstrument) {
48    
49      /**              ::gig::MidiRule* rule;
50       * Dispatch and handle all events in this audio fragment for the given              for (int i = 0 ; (rule = pChannel->pInstrument->GetMidiRule(i)) ; i++) {
      * 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.)  
         pEngineChannel->ImportEvents(Samples);  
   
         // process events  
         {  
             RTList<Event>::Iterator itEvent = pEngineChannel->pEvents->first();  
             RTList<Event>::Iterator end     = pEngineChannel->pEvents->end();  
             for (; itEvent != end; ++itEvent) {  
                 switch (itEvent->Type) {  
                     case Event::type_note_on:  
                         dmsg(5,("Engine: Note on received\n"));  
                         ProcessNoteOn((EngineChannel*)itEvent->pEngineChannel, itEvent);  
                         break;  
                     case Event::type_note_off:  
                         dmsg(5,("Engine: Note off received\n"));  
                         ProcessNoteOff((EngineChannel*)itEvent->pEngineChannel, itEvent);  
                         break;  
                     case Event::type_control_change:  
                         dmsg(5,("Engine: MIDI CC received\n"));  
                         ProcessControlChange((EngineChannel*)itEvent->pEngineChannel, itEvent);  
                         break;  
                     case Event::type_pitchbend:  
                         dmsg(5,("Engine: Pitchbend received\n"));  
                         ProcessPitchbend((EngineChannel*)itEvent->pEngineChannel, itEvent);  
                         break;  
                 }  
             }  
         }  
51    
52          // reset voice stealing for the next engine channel (or next audio fragment)                  if (::gig::MidiRuleCtrlTrigger* ctrlTrigger =
53          itLastStolenVoice         = RTList<Voice>::Iterator();                      dynamic_cast< ::gig::MidiRuleCtrlTrigger*>(rule)) {
54          itLastStolenVoiceGlobally = RTList<Voice>::Iterator();                      if (itControlChangeEvent->Param.CC.Controller ==
55          iuiLastStolenKey          = RTList<uint>::Iterator();                          ctrlTrigger->ControllerNumber) {
         iuiLastStolenKeyGlobally  = RTList<uint>::Iterator();  
         pLastStolenChannel        = NULL;  
     }  
56    
57      /**                          uint8_t oldCCValue = pChannel->ControllerTable[
58       * Render all 'normal' voices (that is voices which were not stolen in                              itControlChangeEvent->Param.CC.Controller];
59       * this fragment) on the given engine channel.                          uint8_t newCCValue = itControlChangeEvent->Param.CC.Value;
      *  
      * @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) {  
         #if !CONFIG_PROCESS_MUTED_CHANNELS  
         if (pEngineChannel->GetMute()) return; // skip if sampler channel is muted  
         #endif  
   
         uint voiceCount = 0;  
         uint streamCount = 0;  
         RTList<uint>::Iterator iuiKey = pEngineChannel->pActiveKeys->first();  
         RTList<uint>::Iterator end    = pEngineChannel->pActiveKeys->end();  
         while (iuiKey != end) { // iterate through all active keys  
             midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[*iuiKey];  
             ++iuiKey;  
   
             RTList<Voice>::Iterator itVoice     = pKey->pActiveVoices->first();  
             RTList<Voice>::Iterator itVoicesEnd = pKey->pActiveVoices->end();  
             for (; itVoice != itVoicesEnd; ++itVoice) { // iterate through all voices on this key  
                 // now render current voice  
                 itVoice->Render(Samples);  
                 if (itVoice->IsActive()) { // still active  
                     if (!itVoice->Orphan) {  
                         *(pEngineChannel->pDimRegionsInUse->allocAppend()) = itVoice->pDimRgn;  
                     }  
                     ActiveVoiceCountTemp++;  
                     voiceCount++;  
60    
61                      if (itVoice->PlaybackState == Voice::playback_state_disk) {                          for (int i = 0 ; i < ctrlTrigger->Triggers ; i++) {
62                          if ((itVoice->DiskStreamRef).State != Stream::state_unused) streamCount++;                              ::gig::MidiRuleCtrlTrigger::trigger_t* pTrigger =
63                      }                                    &ctrlTrigger->pTriggers[i];
                 }  else { // voice reached end, is now inactive  
                     FreeVoice(pEngineChannel, itVoice); // remove voice from the list of active voices  
                 }  
             }  
         }  
64    
65          pEngineChannel->SetVoiceCount(voiceCount);                              // check if the controller has passed the
66          pEngineChannel->SetDiskStreamCount(streamCount);                              // trigger point in the right direction
67      }                              if ((pTrigger->Descending &&
68                                     oldCCValue > pTrigger->TriggerPoint &&
69                                     newCCValue <= pTrigger->TriggerPoint) ||
70                                    (!pTrigger->Descending &&
71                                     oldCCValue < pTrigger->TriggerPoint &&
72                                     newCCValue >= pTrigger->TriggerPoint)) {
73    
74      /**                                  RTList<Event>::Iterator itNewEvent = pGlobalEvents->allocAppend();
75       * Render all stolen voices (only voices which were stolen in this                                  if (itNewEvent) {
76       * fragment) on the given engine channel. Stolen voices are rendered                                      *itNewEvent = *itControlChangeEvent;
77       * after all normal voices have been rendered; this is needed to render                                      itNewEvent->Param.Note.Key = pTrigger->Key;
      * 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<Event>::Iterator itVoiceStealEvent = pVoiceStealingQueue->first();  
         RTList<Event>::Iterator end               = pVoiceStealingQueue->end();  
         for (; itVoiceStealEvent != end; ++itVoiceStealEvent) {  
             EngineChannel* pEngineChannel = (EngineChannel*) itVoiceStealEvent->pEngineChannel;  
             if (!pEngineChannel->pInstrument) continue; // ignore if no instrument loaded  
             Pool<Voice>::Iterator itNewVoice =  
                 LaunchVoice(pEngineChannel, itVoiceStealEvent, itVoiceStealEvent->Param.Note.Layer, itVoiceStealEvent->Param.Note.ReleaseTrigger, false, false);  
             if (itNewVoice) {  
                 itNewVoice->Render(Samples);  
                 if (itNewVoice->IsActive()) { // still active  
                     *(pEngineChannel->pDimRegionsInUse->allocAppend()) = itNewVoice->pDimRgn;  
                     ActiveVoiceCountTemp++;  
                     pEngineChannel->SetVoiceCount(pEngineChannel->GetVoiceCount() + 1);  
   
                     if (itNewVoice->PlaybackState == Voice::playback_state_disk) {  
                         if (itNewVoice->DiskStreamRef.State != Stream::state_unused) {  
                             pEngineChannel->SetDiskStreamCount(pEngineChannel->GetDiskStreamCount() + 1);  
                         }  
                     }  
                 } else { // voice reached end, is now inactive  
                     FreeVoice(pEngineChannel, itNewVoice); // remove voice from the list of active voices  
                 }  
             }  
             else dmsg(1,("gig::Engine: ERROR, voice stealing didn't work out!\n"));  
78    
79              // we need to clear the key's event list explicitly here in case key was never active                                      if (pTrigger->NoteOff || pTrigger->Velocity == 0) {
80              midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[itVoiceStealEvent->Param.Note.Key];                                          itNewEvent->Type = Event::type_note_off;
81              pKey->VoiceTheftsQueued--;                                          itNewEvent->Param.Note.Velocity = 100;
             if (!pKey->Active && !pKey->VoiceTheftsQueued) pKey->pEvents->clear();  
         }  
     }  
82    
83      /**                                          ProcessNoteOff(pEngineChannel, itNewEvent);
84       * Will be called in case the respective engine channel sports FX send                                      } else {
85       * channels. In this particular case, engine channel local buffers are                                          itNewEvent->Type = Event::type_note_on;
86       * used to render and mix all voices to. This method is responsible for                                          //TODO: if Velocity is 255, the triggered velocity should
87       * copying the audio data from those local buffers to the master audio                                          // depend on how fast the controller is moving
88       * output channels as well as to the FX send audio output channels with                                          itNewEvent->Param.Note.Velocity =
89       * their respective FX send levels.                                              pTrigger->Velocity == 255 ? 100 :
90       *                                              pTrigger->Velocity;
      * @param pEngineChannel - engine channel from which audio should be  
      *                         routed  
      * @param Samples        - amount of sample points to be routed in  
      *                         this audio fragment cycle  
      */  
     void Engine::RouteAudio(EngineChannel* pEngineChannel, uint Samples) {  
         // route dry signal  
         {  
             AudioChannel* pDstL = pAudioOutputDevice->Channel(pEngineChannel->AudioDeviceChannelLeft);  
             AudioChannel* pDstR = pAudioOutputDevice->Channel(pEngineChannel->AudioDeviceChannelRight);  
             pEngineChannel->pChannelLeft->MixTo(pDstL, Samples);  
             pEngineChannel->pChannelRight->MixTo(pDstR, Samples);  
         }  
         // route FX send signal  
         {  
             for (int iFxSend = 0; iFxSend < pEngineChannel->GetFxSendCount(); iFxSend++) {  
                 FxSend* pFxSend = pEngineChannel->GetFxSend(iFxSend);  
                 for (int iChan = 0; iChan < 2; ++iChan) {  
                     AudioChannel* pSource =  
                         (iChan)  
                             ? pEngineChannel->pChannelRight  
                             : pEngineChannel->pChannelLeft;  
                     const int iDstChan = pFxSend->DestinationChannel(iChan);  
                     if (iDstChan < 0) {  
                         dmsg(1,("Engine::RouteAudio() Error: invalid FX send (%s) destination channel (%d->%d)", ((iChan) ? "R" : "L"), iChan, iDstChan));  
                         goto channel_cleanup;  
                     }  
                     AudioChannel* pDstChan = NULL;  
                     if (pFxSend->DestinationMasterEffectChain() >= 0) { // fx send routed to an internal master effect  
                         EffectChain* pEffectChain =  
                             pAudioOutputDevice->MasterEffectChain(  
                                 pFxSend->DestinationMasterEffectChain()  
                             );  
                         if (!pEffectChain) {  
                             dmsg(1,("Engine::RouteAudio() Error: invalid FX send (%s) destination effect chain %d", ((iChan) ? "R" : "L"), pFxSend->DestinationMasterEffectChain()));  
                             goto channel_cleanup;  
                         }  
                         Effect* pEffect =  
                             pEffectChain->GetEffect(  
                                 pFxSend->DestinationMasterEffect()  
                             );  
                         if (!pEffect) {  
                             dmsg(1,("Engine::RouteAudio() Error: invalid FX send (%s) destination effect %d of effect chain %d", ((iChan) ? "R" : "L"), pFxSend->DestinationMasterEffect(), pFxSend->DestinationMasterEffectChain()));  
                             goto channel_cleanup;  
                         }  
                         pDstChan = pEffect->InputChannel(iDstChan);  
                     } else { // FX send routed directly to an audio output channel  
                         pDstChan = pAudioOutputDevice->Channel(iDstChan);  
                     }  
                     if (!pDstChan) {  
                         dmsg(1,("Engine::RouteAudio() Error: invalid FX send (%s) destination channel (%d->%d)", ((iChan) ? "R" : "L"), iChan, iDstChan));  
                         goto channel_cleanup;  
                     }  
                     pSource->MixTo(pDstChan, Samples, pFxSend->Level());  
                 }  
             }  
         }  
         channel_cleanup:  
         // reset buffers with silence (zero out) for the next audio cycle  
         pEngineChannel->pChannelLeft->Clear();  
         pEngineChannel->pChannelRight->Clear();  
     }  
91    
92      /**                                          ProcessNoteOn(pEngineChannel, itNewEvent);
93       * Free all keys which have turned inactive in this audio fragment, from                                      }
94       * the list of active keys and clear all event lists on that engine                                  }
95       * channel.                                  else dmsg(1,("Event pool emtpy!\n"));
96       *                              }
      * @param pEngineChannel - engine channel to cleanup  
      */  
     void Engine::PostProcess(EngineChannel* pEngineChannel) {  
         // free all keys which have no active voices left  
         {  
             RTList<uint>::Iterator iuiKey = pEngineChannel->pActiveKeys->first();  
             RTList<uint>::Iterator end    = pEngineChannel->pActiveKeys->end();  
             while (iuiKey != end) { // iterate through all active keys  
                 midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[*iuiKey];  
                 ++iuiKey;  
                 if (pKey->pActiveVoices->isEmpty()) FreeKey(pEngineChannel, pKey);  
                 #if CONFIG_DEVMODE  
                 else { // just a sanity check for debugging  
                     RTList<Voice>::Iterator itVoice     = pKey->pActiveVoices->first();  
                     RTList<Voice>::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"));  
97                          }                          }
98                      }                      }
99                  }                  }
                 #endif // CONFIG_DEVMODE  
100              }              }
101          }          }
102    
103          // empty the engine channel's own event lists          // update controller value in the engine channel's controller table
104          pEngineChannel->ClearEventLists();          pChannel->ControllerTable[itControlChangeEvent->Param.CC.Controller] = itControlChangeEvent->Param.CC.Value;
     }  
105    
106      /**          ProcessHardcodedControllers(pEngineChannel, itControlChangeEvent);
      *  Will be called by the MIDI input device whenever a MIDI system  
      *  exclusive message has arrived.  
      *  
      *  @param pData - pointer to sysex data  
      *  @param Size  - lenght of sysex data (in bytes)  
      *  @param pSender - the MIDI input port on which the SysEx message was  
      *                   received  
      */  
     void Engine::SendSysex(void* pData, uint Size, MidiInputPort* pSender) {  
         Event event             = pEventGenerator->CreateEvent();  
         event.Type              = Event::type_sysex;  
         event.Param.Sysex.Size  = Size;  
         event.pEngineChannel    = NULL; // as Engine global event  
         event.pMidiInputPort    = pSender;  
         if (pEventQueue->write_space() > 0) {  
             if (pSysexBuffer->write_space() >= Size) {  
                 // copy sysex data to input buffer  
                 uint toWrite = Size;  
                 uint8_t* pPos = (uint8_t*) pData;  
                 while (toWrite) {  
                     const uint writeNow = RTMath::Min(toWrite, pSysexBuffer->write_space_to_end());  
                     pSysexBuffer->write(pPos, writeNow);  
                     toWrite -= writeNow;  
                     pPos    += writeNow;  
107    
108                  }          // handle FX send controllers
109                  // finally place sysex event into input event queue          ProcessFxSendControllers(pChannel, itControlChangeEvent);
                 pEventQueue->push(&event);  
             }  
             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!"));  
110      }      }
111    
112      /**      DiskThread* Engine::CreateDiskThread() {
113       *  Assigns and triggers a new voice for the respective MIDI key.          return new DiskThread (
114       *              iMaxDiskStreams,
115       *  @param pEngineChannel - engine channel on which this event occured on              ((pAudioOutputDevice->MaxSamplesPerCycle() << CONFIG_MAX_PITCH) << 1) + 6, //FIXME: assuming stereo
116       *  @param itNoteOnEvent - key, velocity and time stamp of the event              &instruments
117       */          );
118      void Engine::ProcessNoteOn(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNoteOnEvent) {      }
119          #if !CONFIG_PROCESS_MUTED_CHANNELS  
120          if (pEngineChannel->GetMute()) return; // skip if sampler channel is muted      void Engine::TriggerNewVoices (
121          #endif          LinuxSampler::EngineChannel* pEngineChannel,
122            RTList<Event>::Iterator&     itNoteOnEvent,
123          if (!pEngineChannel->pInstrument) return; // ignore if no instrument loaded          bool                         HandleKeyGroupConflicts
124        ) {
125          //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          EngineChannel* pChannel = static_cast<EngineChannel*>(pEngineChannel);
126          itNoteOnEvent->Param.Note.Key += pEngineChannel->GlobalTranspose;          // first, get total amount of required voices (dependant on amount of layers)
127            ::gig::Region* pRegion = pChannel->pInstrument->GetRegion(itNoteOnEvent->Param.Note.Key);
128          const int key = itNoteOnEvent->Param.Note.Key;          if (pRegion && !RegionSuspended(pRegion)) {
129          midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[key];              int voicesRequired = pRegion->Layers;
130                // now launch the required amount of voices
131          // move note on event to the key's own event list              for (int i = 0; i < voicesRequired; i++)
132          RTList<Event>::Iterator itNoteOnEventOnKeyList = itNoteOnEvent.moveToEndOf(pKey->pEvents);                  LaunchVoice(pChannel, itNoteOnEvent, i, false, true, HandleKeyGroupConflicts);
   
         // if Solo Mode then kill all already active voices  
         if (pEngineChannel->SoloMode) {  
             Pool<uint>::Iterator itYoungestKey = pEngineChannel->pActiveKeys->last();  
             if (itYoungestKey) {  
                 const int iYoungestKey = *itYoungestKey;  
                 const midi_key_info_t* pOtherKey = &pEngineChannel->pMIDIKeyInfo[iYoungestKey];  
                 if (pOtherKey->Active) {  
                     // get final portamento position of currently active voice  
                     if (pEngineChannel->PortamentoMode) {  
                         RTList<Voice>::Iterator itVoice = pOtherKey->pActiveVoices->last();  
                         if (itVoice) itVoice->UpdatePortamentoPos(itNoteOnEventOnKeyList);  
                     }  
                     // kill all voices on the (other) key  
                     RTList<Voice>::Iterator itVoiceToBeKilled = pOtherKey->pActiveVoices->first();  
                     RTList<Voice>::Iterator end               = pOtherKey->pActiveVoices->end();  
                     for (; itVoiceToBeKilled != end; ++itVoiceToBeKilled) {  
                         if (itVoiceToBeKilled->Type != Voice::type_release_trigger)  
                             itVoiceToBeKilled->Kill(itNoteOnEventOnKeyList);  
                     }  
                 }  
             }  
             // set this key as 'currently active solo key'  
             pEngineChannel->SoloKey = key;  
         }  
   
         // Change key dimension value if key is in keyswitching area  
         {  
             const ::gig::Instrument* pInstrument = pEngineChannel->pInstrument;  
             if (key >= pInstrument->DimensionKeyRange.low && key <= pInstrument->DimensionKeyRange.high)  
                 pEngineChannel->CurrentKeyDimension = float(key - pInstrument->DimensionKeyRange.low) /  
                     (pInstrument->DimensionKeyRange.high - pInstrument->DimensionKeyRange.low + 1);  
         }  
   
         pKey->KeyPressed = true; // the MIDI key was now pressed down  
         pKey->Velocity   = itNoteOnEventOnKeyList->Param.Note.Velocity;  
         pKey->NoteOnTime = FrameTime + itNoteOnEventOnKeyList->FragmentPos(); // will be used to calculate note length  
   
         // cancel release process of voices on this key if needed  
         if (pKey->Active && !pEngineChannel->SustainPedal) {  
             RTList<Event>::Iterator itCancelReleaseEvent = pKey->pEvents->allocAppend();  
             if (itCancelReleaseEvent) {  
                 *itCancelReleaseEvent = *itNoteOnEventOnKeyList;         // copy event  
                 itCancelReleaseEvent->Type = Event::type_cancel_release; // transform event type  
             }  
             else dmsg(1,("Event pool emtpy!\n"));  
         }  
   
         // allocate and trigger new voice(s) for the key  
         {  
             // first, get total amount of required voices (dependant on amount of layers)  
             ::gig::Region* pRegion = pEngineChannel->pInstrument->GetRegion(itNoteOnEventOnKeyList->Param.Note.Key);  
             if (pRegion && !RegionSuspended(pRegion)) {  
                 int voicesRequired = pRegion->Layers;  
                 // now launch the required amount of voices  
                 for (int i = 0; i < voicesRequired; i++)  
                     LaunchVoice(pEngineChannel, itNoteOnEventOnKeyList, i, false, true, true);  
             }  
133          }          }
   
         // 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);  
   
         if (!pEngineChannel->SoloMode || pEngineChannel->PortamentoPos < 0.0f) pEngineChannel->PortamentoPos = (float) key;  
         pKey->RoundRobinIndex++;  
134      }      }
135    
136      /**      void Engine::TriggerReleaseVoices (
137       *  Releases the voices on the given key if sustain pedal is not pressed.          LinuxSampler::EngineChannel*  pEngineChannel,
138       *  If sustain is pressed, the release of the note will be postponed until          RTList<Event>::Iterator&      itNoteOffEvent
139       *  sustain pedal will be released or voice turned inactive by itself (e.g.      ) {
140       *  due to completion of sample playback).          EngineChannel* pChannel = static_cast<EngineChannel*>(pEngineChannel);
141       *          MidiKey* pKey = &pChannel->pMIDIKeyInfo[itNoteOffEvent->Param.Note.Key];
142       *  @param pEngineChannel - engine channel on which this event occured on          // first, get total amount of required voices (dependant on amount of layers)
143       *  @param itNoteOffEvent - key, velocity and time stamp of the event          ::gig::Region* pRegion = pChannel->pInstrument->GetRegion(itNoteOffEvent->Param.Note.Key);
144       */          if (pRegion) {
145      void Engine::ProcessNoteOff(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNoteOffEvent) {              int voicesRequired = pRegion->Layers;
146          #if !CONFIG_PROCESS_MUTED_CHANNELS  
147          if (pEngineChannel->GetMute()) return; // skip if sampler channel is muted              // MIDI note-on velocity is used instead of note-off velocity
148          #endif              itNoteOffEvent->Param.Note.Velocity = pKey->Velocity;
149    
150          //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              // now launch the required amount of voices
151          itNoteOffEvent->Param.Note.Key += pEngineChannel->GlobalTranspose;              for (int i = 0; i < voicesRequired; i++)
152                    LaunchVoice(pChannel, itNoteOffEvent, i, true, false, false); //FIXME: for the moment we don't perform voice stealing for release triggered samples
         const int iKey = itNoteOffEvent->Param.Note.Key;  
         midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[iKey];  
         pKey->KeyPressed = false; // the MIDI key was now released  
   
         // move event to the key's own event list  
         RTList<Event>::Iterator itNoteOffEventOnKeyList = itNoteOffEvent.moveToEndOf(pKey->pEvents);  
   
         bool bShouldRelease = pKey->Active && ShouldReleaseVoice(pEngineChannel, itNoteOffEventOnKeyList->Param.Note.Key);  
   
         // in case Solo Mode is enabled, kill all voices on this key and respawn a voice on the highest pressed key (if any)  
         if (pEngineChannel->SoloMode && pEngineChannel->pInstrument) { //TODO: this feels like too much code just for handling solo mode :P  
             bool bOtherKeysPressed = false;  
             if (iKey == pEngineChannel->SoloKey) {  
                 pEngineChannel->SoloKey = -1;  
                 // if there's still a key pressed down, respawn a voice (group) on the highest key  
                 for (int i = 127; i > 0; i--) {  
                     midi_key_info_t* pOtherKey = &pEngineChannel->pMIDIKeyInfo[i];  
                     if (pOtherKey->KeyPressed) {  
                         bOtherKeysPressed = true;  
                         // make the other key the new 'currently active solo key'  
                         pEngineChannel->SoloKey = i;  
                         // get final portamento position of currently active voice  
                         if (pEngineChannel->PortamentoMode) {  
                             RTList<Voice>::Iterator itVoice = pKey->pActiveVoices->first();  
                             if (itVoice) itVoice->UpdatePortamentoPos(itNoteOffEventOnKeyList);  
                         }  
                         // create a pseudo note on event  
                         RTList<Event>::Iterator itPseudoNoteOnEvent = pOtherKey->pEvents->allocAppend();  
                         if (itPseudoNoteOnEvent) {  
                             // copy event  
                             *itPseudoNoteOnEvent = *itNoteOffEventOnKeyList;  
                             // transform event to a note on event  
                             itPseudoNoteOnEvent->Type                = Event::type_note_on;  
                             itPseudoNoteOnEvent->Param.Note.Key      = i;  
                             itPseudoNoteOnEvent->Param.Note.Velocity = pOtherKey->Velocity;  
                             // allocate and trigger new voice(s) for the other key  
                             {  
                                 // first, get total amount of required voices (dependant on amount of layers)  
                                 ::gig::Region* pRegion = pEngineChannel->pInstrument->GetRegion(i);  
                                 if (pRegion) {  
                                     int voicesRequired = pRegion->Layers;  
                                     // now launch the required amount of voices  
                                     for (int iLayer = 0; iLayer < voicesRequired; iLayer++)  
                                         LaunchVoice(pEngineChannel, itPseudoNoteOnEvent, iLayer, false, true, false);  
                                 }  
                             }  
                             // if neither a voice was spawned or postponed then remove note on event from key again  
                             if (!pOtherKey->Active && !pOtherKey->VoiceTheftsQueued)  
                                 pOtherKey->pEvents->free(itPseudoNoteOnEvent);  
   
                         } else dmsg(1,("Could not respawn voice, no free event left\n"));  
                         break; // done  
                     }  
                 }  
             }  
             if (bOtherKeysPressed) {  
                 if (pKey->Active) { // kill all voices on this key  
                     bShouldRelease = false; // no need to release, as we kill it here  
                     RTList<Voice>::Iterator itVoiceToBeKilled = pKey->pActiveVoices->first();  
                     RTList<Voice>::Iterator end               = pKey->pActiveVoices->end();  
                     for (; itVoiceToBeKilled != end; ++itVoiceToBeKilled) {  
                         if (itVoiceToBeKilled->Type != Voice::type_release_trigger)  
                             itVoiceToBeKilled->Kill(itNoteOffEventOnKeyList);  
                     }  
                 }  
             } else pEngineChannel->PortamentoPos = -1.0f;  
153          }          }
   
         // if no solo mode (the usual case) or if solo mode and no other key pressed, then release voices on this key if needed  
         if (bShouldRelease) {  
             itNoteOffEventOnKeyList->Type = Event::type_release; // transform event type  
   
             // spawn release triggered voice(s) if needed  
             if (pKey->ReleaseTrigger && pEngineChannel->pInstrument) {  
                 // 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;  
   
                     // MIDI note-on velocity is used instead of note-off velocity  
                     itNoteOffEventOnKeyList->Param.Note.Velocity = pKey->Velocity;  
   
                     // now launch the required amount of voices  
                     for (int i = 0; i < voicesRequired; i++)  
                         LaunchVoice(pEngineChannel, itNoteOffEventOnKeyList, i, true, false, false); //FIXME: for the moment we don't perform voice stealing for release triggered samples  
                 }  
                 pKey->ReleaseTrigger = false;  
             }  
         }  
   
         // if neither a voice was spawned or postponed on this key then remove note off event from key again  
         if (!pKey->Active && !pKey->VoiceTheftsQueued)  
             pKey->pEvents->free(itNoteOffEventOnKeyList);  
154      }      }
155    
156      /**      Pool<Voice>::Iterator Engine::LaunchVoice (
157       *  Moves pitchbend event from the general (input) event list to the engine          LinuxSampler::EngineChannel*  pEngineChannel,
158       *  channel's event list. It will actually processed later by the          Pool<Event>::Iterator&        itNoteOnEvent,
159       *  respective voice.          int                           iLayer,
160       *          bool                          ReleaseTriggerVoice,
161       *  @param pEngineChannel - engine channel on which this event occured on          bool                          VoiceStealing,
162       *  @param itPitchbendEvent - absolute pitch value and time stamp of the event          bool                          HandleKeyGroupConflicts
163       */      ) {
164      void Engine::ProcessPitchbend(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itPitchbendEvent) {          EngineChannel* pChannel = static_cast<EngineChannel*>(pEngineChannel);
165          pEngineChannel->Pitch = itPitchbendEvent->Param.Pitch.Pitch; // store current pitch value          int MIDIKey = itNoteOnEvent->Param.Note.Key;
166      }          EngineChannel::MidiKey* pKey  = &pChannel->pMIDIKeyInfo[MIDIKey];
167            ::gig::Region* pRegion = pChannel->pInstrument->GetRegion(MIDIKey);
     /**  
      *  Allocates and triggers a new voice. This method will usually be  
      *  called by the ProcessNoteOn() method and by the voices itself  
      *  (e.g. to spawn further voices on the same key for layered sounds).  
      *  
      *  @param pEngineChannel      - engine channel on which this event occured on  
      *  @param itNoteOnEvent       - key, velocity and time stamp of the event  
      *  @param iLayer              - layer index for the new voice (optional - only  
      *                               in case of layered sounds of course)  
      *  @param ReleaseTriggerVoice - if new voice is a release triggered voice  
      *                               (optional, default = false)  
      *  @param VoiceStealing       - if voice stealing should be performed  
      *                               when there is no free voice  
      *                               (optional, default = true)  
      *  @param HandleKeyGroupConflicts - if voices should be killed due to a  
      *                                   key group conflict  
      *  @returns pointer to new voice or NULL if there was no free voice or  
      *           if the voice wasn't triggered (for example when no region is  
      *           defined for the given key).  
      */  
     Pool<Voice>::Iterator Engine::LaunchVoice(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNoteOnEvent, int iLayer, bool ReleaseTriggerVoice, bool VoiceStealing, bool HandleKeyGroupConflicts) {  
         int MIDIKey            = itNoteOnEvent->Param.Note.Key;  
         midi_key_info_t* pKey  = &pEngineChannel->pMIDIKeyInfo[MIDIKey];  
         ::gig::Region* pRegion = pEngineChannel->pInstrument->GetRegion(MIDIKey);  
168    
169          // if nothing defined for this key          // if nothing defined for this key
170          if (!pRegion) return Pool<Voice>::Iterator(); // nothing to do          if (!pRegion) return Pool<Voice>::Iterator(); // nothing to do
# Line 1246  namespace LinuxSampler { namespace gig { Line 176  namespace LinuxSampler { namespace gig {
176          // handle key group (a.k.a. exclusive group) conflicts          // handle key group (a.k.a. exclusive group) conflicts
177          if (HandleKeyGroupConflicts) {          if (HandleKeyGroupConflicts) {
178              if (iKeyGroup) { // if this voice / key belongs to a key group              if (iKeyGroup) { // if this voice / key belongs to a key group
179                  uint** ppKeyGroup = &pEngineChannel->ActiveKeyGroups[iKeyGroup];                  uint** ppKeyGroup = &pChannel->ActiveKeyGroups[iKeyGroup];
180                  if (*ppKeyGroup) { // if there's already an active key in that key group                  if (*ppKeyGroup) { // if there's already an active key in that key group
181                      midi_key_info_t* pOtherKey = &pEngineChannel->pMIDIKeyInfo[**ppKeyGroup];                      EngineChannel::MidiKey* pOtherKey = &pChannel->pMIDIKeyInfo[**ppKeyGroup];
182                      // kill all voices on the (other) key                      // kill all voices on the (other) key
183                      RTList<Voice>::Iterator itVoiceToBeKilled = pOtherKey->pActiveVoices->first();                      RTList<Voice>::Iterator itVoiceToBeKilled = pOtherKey->pActiveVoices->first();
184                      RTList<Voice>::Iterator end               = pOtherKey->pActiveVoices->end();                      RTList<Voice>::Iterator end               = pOtherKey->pActiveVoices->end();
# Line 1280  namespace LinuxSampler { namespace gig { Line 210  namespace LinuxSampler { namespace gig {
210                      DimValues[i] = itNoteOnEvent->Param.Note.Velocity;                      DimValues[i] = itNoteOnEvent->Param.Note.Velocity;
211                      break;                      break;
212                  case ::gig::dimension_channelaftertouch:                  case ::gig::dimension_channelaftertouch:
213                      DimValues[i] = pEngineChannel->ControllerTable[128];                      DimValues[i] = pChannel->ControllerTable[128];
214                      break;                      break;
215                  case ::gig::dimension_releasetrigger:                  case ::gig::dimension_releasetrigger:
216                      VoiceType = (ReleaseTriggerVoice) ? Voice::type_release_trigger : (!iLayer) ? Voice::type_release_trigger_required : Voice::type_normal;                      VoiceType = (ReleaseTriggerVoice) ? Voice::type_release_trigger : (!iLayer) ? Voice::type_release_trigger_required : Voice::type_normal;
217                      DimValues[i] = (uint) ReleaseTriggerVoice;                      DimValues[i] = (uint) ReleaseTriggerVoice;
218                      break;                      break;
219                  case ::gig::dimension_keyboard:                  case ::gig::dimension_keyboard:
220                      DimValues[i] = (uint) (pEngineChannel->CurrentKeyDimension * pRegion->pDimensionDefinitions[i].zones);                      DimValues[i] = (uint) (pChannel->CurrentKeyDimension * pRegion->pDimensionDefinitions[i].zones);
221                      break;                      break;
222                  case ::gig::dimension_roundrobin:                  case ::gig::dimension_roundrobin:
223                      DimValues[i] = (uint) pEngineChannel->pMIDIKeyInfo[MIDIKey].RoundRobinIndex; // incremented for each note on                      DimValues[i] = (uint) pChannel->pMIDIKeyInfo[MIDIKey].RoundRobinIndex; // incremented for each note on
224                      break;                      break;
225                  case ::gig::dimension_random:                  case ::gig::dimension_random:
226                      RandomSeed   = RandomSeed * 1103515245 + 12345; // classic pseudo random number generator                      RandomSeed   = RandomSeed * 1103515245 + 12345; // classic pseudo random number generator
227                      DimValues[i] = (uint) RandomSeed >> (32 - pRegion->pDimensionDefinitions[i].bits); // highest bits are most random                      DimValues[i] = (uint) RandomSeed >> (32 - pRegion->pDimensionDefinitions[i].bits); // highest bits are most random
228                      break;                      break;
229                  case ::gig::dimension_modwheel:                  case ::gig::dimension_modwheel:
230                      DimValues[i] = pEngineChannel->ControllerTable[1];                      DimValues[i] = pChannel->ControllerTable[1];
231                      break;                      break;
232                  case ::gig::dimension_breath:                  case ::gig::dimension_breath:
233                      DimValues[i] = pEngineChannel->ControllerTable[2];                      DimValues[i] = pChannel->ControllerTable[2];
234                      break;                      break;
235                  case ::gig::dimension_foot:                  case ::gig::dimension_foot:
236                      DimValues[i] = pEngineChannel->ControllerTable[4];                      DimValues[i] = pChannel->ControllerTable[4];
237                      break;                      break;
238                  case ::gig::dimension_portamentotime:                  case ::gig::dimension_portamentotime:
239                      DimValues[i] = pEngineChannel->ControllerTable[5];                      DimValues[i] = pChannel->ControllerTable[5];
240                      break;                      break;
241                  case ::gig::dimension_effect1:                  case ::gig::dimension_effect1:
242                      DimValues[i] = pEngineChannel->ControllerTable[12];                      DimValues[i] = pChannel->ControllerTable[12];
243                      break;                      break;
244                  case ::gig::dimension_effect2:                  case ::gig::dimension_effect2:
245                      DimValues[i] = pEngineChannel->ControllerTable[13];                      DimValues[i] = pChannel->ControllerTable[13];
246                      break;                      break;
247                  case ::gig::dimension_genpurpose1:                  case ::gig::dimension_genpurpose1:
248                      DimValues[i] = pEngineChannel->ControllerTable[16];                      DimValues[i] = pChannel->ControllerTable[16];
249                      break;                      break;
250                  case ::gig::dimension_genpurpose2:                  case ::gig::dimension_genpurpose2:
251                      DimValues[i] = pEngineChannel->ControllerTable[17];                      DimValues[i] = pChannel->ControllerTable[17];
252                      break;                      break;
253                  case ::gig::dimension_genpurpose3:                  case ::gig::dimension_genpurpose3:
254                      DimValues[i] = pEngineChannel->ControllerTable[18];                      DimValues[i] = pChannel->ControllerTable[18];
255                      break;                      break;
256                  case ::gig::dimension_genpurpose4:                  case ::gig::dimension_genpurpose4:
257                      DimValues[i] = pEngineChannel->ControllerTable[19];                      DimValues[i] = pChannel->ControllerTable[19];
258                      break;                      break;
259                  case ::gig::dimension_sustainpedal:                  case ::gig::dimension_sustainpedal:
260                      DimValues[i] = pEngineChannel->ControllerTable[64];                      DimValues[i] = pChannel->ControllerTable[64];
261                      break;                      break;
262                  case ::gig::dimension_portamento:                  case ::gig::dimension_portamento:
263                      DimValues[i] = pEngineChannel->ControllerTable[65];                      DimValues[i] = pChannel->ControllerTable[65];
264                      break;                      break;
265                  case ::gig::dimension_sostenutopedal:                  case ::gig::dimension_sostenutopedal:
266                      DimValues[i] = pEngineChannel->ControllerTable[66];                      DimValues[i] = pChannel->ControllerTable[66];
267                      break;                      break;
268                  case ::gig::dimension_softpedal:                  case ::gig::dimension_softpedal:
269                      DimValues[i] = pEngineChannel->ControllerTable[67];                      DimValues[i] = pChannel->ControllerTable[67];
270                      break;                      break;
271                  case ::gig::dimension_genpurpose5:                  case ::gig::dimension_genpurpose5:
272                      DimValues[i] = pEngineChannel->ControllerTable[80];                      DimValues[i] = pChannel->ControllerTable[80];
273                      break;                      break;
274                  case ::gig::dimension_genpurpose6:                  case ::gig::dimension_genpurpose6:
275                      DimValues[i] = pEngineChannel->ControllerTable[81];                      DimValues[i] = pChannel->ControllerTable[81];
276                      break;                      break;
277                  case ::gig::dimension_genpurpose7:                  case ::gig::dimension_genpurpose7:
278                      DimValues[i] = pEngineChannel->ControllerTable[82];                      DimValues[i] = pChannel->ControllerTable[82];
279                      break;                      break;
280                  case ::gig::dimension_genpurpose8:                  case ::gig::dimension_genpurpose8:
281                      DimValues[i] = pEngineChannel->ControllerTable[83];                      DimValues[i] = pChannel->ControllerTable[83];
282                      break;                      break;
283                  case ::gig::dimension_effect1depth:                  case ::gig::dimension_effect1depth:
284                      DimValues[i] = pEngineChannel->ControllerTable[91];                      DimValues[i] = pChannel->ControllerTable[91];
285                      break;                      break;
286                  case ::gig::dimension_effect2depth:                  case ::gig::dimension_effect2depth:
287                      DimValues[i] = pEngineChannel->ControllerTable[92];                      DimValues[i] = pChannel->ControllerTable[92];
288                      break;                      break;
289                  case ::gig::dimension_effect3depth:                  case ::gig::dimension_effect3depth:
290                      DimValues[i] = pEngineChannel->ControllerTable[93];                      DimValues[i] = pChannel->ControllerTable[93];
291                      break;                      break;
292                  case ::gig::dimension_effect4depth:                  case ::gig::dimension_effect4depth:
293                      DimValues[i] = pEngineChannel->ControllerTable[94];                      DimValues[i] = pChannel->ControllerTable[94];
294                      break;                      break;
295                  case ::gig::dimension_effect5depth:                  case ::gig::dimension_effect5depth:
296                      DimValues[i] = pEngineChannel->ControllerTable[95];                      DimValues[i] = pChannel->ControllerTable[95];
297                      break;                      break;
298                  case ::gig::dimension_none:                  case ::gig::dimension_none:
299                      std::cerr << "gig::Engine::LaunchVoice() Error: dimension=none\n" << std::flush;                      std::cerr << "gig::Engine::LaunchVoice() Error: dimension=none\n" << std::flush;
# Line 1387  namespace LinuxSampler { namespace gig { Line 317  namespace LinuxSampler { namespace gig {
317          Pool<Voice>::Iterator itNewVoice = pKey->pActiveVoices->allocAppend();          Pool<Voice>::Iterator itNewVoice = pKey->pActiveVoices->allocAppend();
318          if (itNewVoice) {          if (itNewVoice) {
319              // launch the new voice              // launch the new voice
320              if (itNewVoice->Trigger(pEngineChannel, itNoteOnEvent, pEngineChannel->Pitch, pDimRgn, VoiceType, iKeyGroup) < 0) {              if (itNewVoice->Trigger(pChannel, itNoteOnEvent, pChannel->Pitch, pDimRgn, VoiceType, iKeyGroup) < 0) {
321                  dmsg(4,("Voice not triggered\n"));                  dmsg(4,("Voice not triggered\n"));
322                  pKey->pActiveVoices->free(itNewVoice);                  pKey->pActiveVoices->free(itNewVoice);
323              }              }
# Line 1395  namespace LinuxSampler { namespace gig { Line 325  namespace LinuxSampler { namespace gig {
325                  --VoiceSpawnsLeft;                  --VoiceSpawnsLeft;
326                  if (!pKey->Active) { // mark as active key                  if (!pKey->Active) { // mark as active key
327                      pKey->Active = true;                      pKey->Active = true;
328                      pKey->itSelf = pEngineChannel->pActiveKeys->allocAppend();                      pKey->itSelf = pChannel->pActiveKeys->allocAppend();
329                      *pKey->itSelf = itNoteOnEvent->Param.Note.Key;                      *pKey->itSelf = itNoteOnEvent->Param.Note.Key;
330                  }                  }
331                  if (itNewVoice->KeyGroup) {                  if (itNewVoice->KeyGroup) {
332                      uint** ppKeyGroup = &pEngineChannel->ActiveKeyGroups[itNewVoice->KeyGroup];                      uint** ppKeyGroup = &pChannel->ActiveKeyGroups[itNewVoice->KeyGroup];
333                      *ppKeyGroup = &*pKey->itSelf; // put key as the (new) active key to its key group                      *ppKeyGroup = &*pKey->itSelf; // put key as the (new) active key to its key group
334                  }                  }
335                  if (itNewVoice->Type == Voice::type_release_trigger_required) pKey->ReleaseTrigger = true; // mark key for the need of release triggered voice(s)                  if (itNewVoice->Type == Voice::type_release_trigger_required) pKey->ReleaseTrigger = true; // mark key for the need of release triggered voice(s)
# Line 1408  namespace LinuxSampler { namespace gig { Line 338  namespace LinuxSampler { namespace gig {
338          }          }
339          else if (VoiceStealing) {          else if (VoiceStealing) {
340              // try to steal one voice              // try to steal one voice
341              int result = StealVoice(pEngineChannel, itNoteOnEvent);              int result = StealVoice(pChannel, itNoteOnEvent);
342              if (!result) { // voice stolen successfully              if (!result) { // voice stolen successfully
343                  // put note-on event into voice-stealing queue, so it will be reprocessed after killed voice died                  // put note-on event into voice-stealing queue, so it will be reprocessed after killed voice died
344                  RTList<Event>::Iterator itStealEvent = pVoiceStealingQueue->allocAppend();                  RTList<Event>::Iterator itStealEvent = pVoiceStealingQueue->allocAppend();
# Line 1425  namespace LinuxSampler { namespace gig { Line 355  namespace LinuxSampler { namespace gig {
355          return Pool<Voice>::Iterator(); // no free voice or error          return Pool<Voice>::Iterator(); // no free voice or error
356      }      }
357    
     /**  
      *  Will be called by LaunchVoice() method in case there are no free  
      *  voices left. This method will select and kill one old voice for  
      *  voice stealing and postpone the note-on event until the selected  
      *  voice actually died.  
      *  
      *  @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  
      */  
     int Engine::StealVoice(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNoteOnEvent) {  
         if (VoiceSpawnsLeft <= 0) {  
             dmsg(1,("Max. voice thefts per audio fragment reached (you may raise CONFIG_MAX_VOICES).\n"));  
             return -1;  
         }  
         if (!pEventPool->poolIsEmpty()) {  
   
             RTList<Voice>::Iterator itSelectedVoice;  
   
             // Select one voice for voice stealing  
             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, then procceed with  
                 // 'oldestkey' algorithm  
                 case voice_steal_algo_oldestvoiceonkey: {  
                     midi_key_info_t* pSelectedKey = &pEngineChannel->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;  
                 } // 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 (this->itLastStolenVoice) {  
                         itSelectedVoice = this->itLastStolenVoice;  
                         do {  
                             ++itSelectedVoice;  
                         } while (itSelectedVoice && !itSelectedVoice->IsStealable()); // proceed iterating if voice was created in this fragment cycle  
                         // found a "stealable" voice ?  
                         if (itSelectedVoice && itSelectedVoice->IsStealable()) {  
                             // remember which voice we stole, so we can simply proceed on next voice stealing  
                             this->itLastStolenVoice = itSelectedVoice;  
                             break; // selection succeeded  
                         }  
                     }  
                     // get (next) oldest key  
                     RTList<uint>::Iterator iuiSelectedKey = (this->iuiLastStolenKey) ? ++this->iuiLastStolenKey : pEngineChannel->pActiveKeys->first();  
                     while (iuiSelectedKey) {  
                         midi_key_info_t* pSelectedKey = &pEngineChannel->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  
                             this->iuiLastStolenKey  = iuiSelectedKey;  
                             this->itLastStolenVoice = itSelectedVoice;  
                             break; // selection succeeded  
                         }  
                         ++iuiSelectedKey; // get next oldest key  
                     }  
                     break;  
                 }  
   
                 // don't steal anything  
                 case voice_steal_algo_none:  
                 default: {  
                     dmsg(1,("No free voice (voice stealing disabled)!\n"));  
                     return -1;  
                 }  
             }  
   
             // 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  
             // (the smaller engine channel number, the higher priority)  
             if (!itSelectedVoice || !itSelectedVoice->IsStealable()) {  
                 EngineChannel* pSelectedChannel;  
                 int            iChannelIndex;  
                 // select engine channel  
                 if (pLastStolenChannel) {  
                     pSelectedChannel = pLastStolenChannel;  
                     iChannelIndex    = pSelectedChannel->iEngineIndexSelf;  
                 } else { // pick the engine channel followed by this engine channel  
                     iChannelIndex    = (pEngineChannel->iEngineIndexSelf + 1) % engineChannels.size();  
                     pSelectedChannel = engineChannels[iChannelIndex];  
                 }  
   
                 // if we already stole in this fragment, try to proceed on same key  
                 if (this->itLastStolenVoiceGlobally) {  
                     itSelectedVoice = this->itLastStolenVoiceGlobally;  
                     do {  
                         ++itSelectedVoice;  
                     } while (itSelectedVoice && !itSelectedVoice->IsStealable()); // proceed iterating if voice was created in this fragment cycle  
                 }  
   
                 #if CONFIG_DEVMODE  
                 EngineChannel* pBegin = pSelectedChannel; // to detect endless loop  
                 #endif // CONFIG_DEVMODE  
   
                 // did we find a 'stealable' voice?  
                 if (itSelectedVoice && itSelectedVoice->IsStealable()) {  
                     // remember which voice we stole, so we can simply proceed on next voice stealing  
                     this->itLastStolenVoiceGlobally = itSelectedVoice;  
                 } else while (true) { // iterate through engine channels  
                     // get (next) oldest key  
                     RTList<uint>::Iterator iuiSelectedKey = (this->iuiLastStolenKeyGlobally) ? ++this->iuiLastStolenKeyGlobally : pSelectedChannel->pActiveKeys->first();  
                     this->iuiLastStolenKeyGlobally = RTList<uint>::Iterator(); // to prevent endless loop (see line above)  
                     while (iuiSelectedKey) {  
                         midi_key_info_t* pSelectedKey = &pSelectedChannel->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 on which engine channel we stole, so we can simply proceed on next voice stealing  
                             this->iuiLastStolenKeyGlobally  = iuiSelectedKey;  
                             this->itLastStolenVoiceGlobally = itSelectedVoice;  
                             this->pLastStolenChannel        = pSelectedChannel;  
                             goto stealable_voice_found; // selection succeeded  
                         }  
                         ++iuiSelectedKey; // get next key on current engine channel  
                     }  
                     // get next engine channel  
                     iChannelIndex    = (iChannelIndex + 1) % engineChannels.size();  
                     pSelectedChannel = 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  
                 }  
             }  
   
             // jump point if a 'stealable' voice was found  
             stealable_voice_found:  
   
             #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);  
   
             --VoiceSpawnsLeft;  
   
             return 0; // success  
         }  
         else {  
             dmsg(1,("Event pool emtpy!\n"));  
             return -1;  
         }  
     }  
   
     /**  
      *  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 pEngineChannel - engine channel on which this event occured on  
      *  @param itVoice - points to the voice to be freed  
      */  
     void Engine::FreeVoice(EngineChannel* pEngineChannel, Pool<Voice>::Iterator& itVoice) {  
         if (itVoice) {  
             midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[itVoice->MIDIKey];  
   
             uint keygroup = itVoice->KeyGroup;  
   
             // if the sample and dimension region belong to an  
             // instrument that is unloaded, tell the disk thread to  
             // release them  
             if (itVoice->Orphan) {  
                 pDiskThread->OrderDeletionOfDimreg(itVoice->pDimRgn);  
             }  
   
             // free the voice object  
             pVoicePool->free(itVoice);  
   
             // if no other voices left and member of a key group, remove from key group  
             if (pKey->pActiveVoices->isEmpty() && keygroup) {  
                 uint** ppKeyGroup = &pEngineChannel->ActiveKeyGroups[keygroup];  
                 if (*ppKeyGroup == &*pKey->itSelf) *ppKeyGroup = NULL; // remove key from key group  
             }  
         }  
         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 pEngineChannel - engine channel on which this event occured on  
      *  @param pKey - key which is now inactive  
      */  
     void Engine::FreeKey(EngineChannel* pEngineChannel, midi_key_info_t* pKey) {  
         if (pKey->pActiveVoices->isEmpty()) {  
             pKey->Active = false;  
             pEngineChannel->pActiveKeys->free(pKey->itSelf); // remove key from list of active keys  
             pKey->itSelf = RTList<uint>::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"));  
     }  
   
     /**  
      *  Reacts on supported control change commands (e.g. pitch bend wheel,  
      *  modulation wheel, aftertouch).  
      *  
      *  @param pEngineChannel - engine channel on which this event occured on  
      *  @param itControlChangeEvent - controller, value and time stamp of the event  
      */  
     void Engine::ProcessControlChange(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itControlChangeEvent) {  
         dmsg(4,("Engine::ContinuousController cc=%d v=%d\n", itControlChangeEvent->Param.CC.Controller, itControlChangeEvent->Param.CC.Value));  
   
         // handle the "control triggered" MIDI rule: a control change  
         // event can trigger a new note on or note off event  
         if (pEngineChannel->pInstrument) {  
   
             ::gig::MidiRule* rule;  
             for (int i = 0 ; (rule = pEngineChannel->pInstrument->GetMidiRule(i)) ; i++) {  
   
                 if (::gig::MidiRuleCtrlTrigger* ctrlTrigger =  
                     dynamic_cast< ::gig::MidiRuleCtrlTrigger*>(rule)) {  
                     if (itControlChangeEvent->Param.CC.Controller ==  
                         ctrlTrigger->ControllerNumber) {  
   
                         uint8_t oldCCValue = pEngineChannel->ControllerTable[  
                             itControlChangeEvent->Param.CC.Controller];  
                         uint8_t newCCValue = itControlChangeEvent->Param.CC.Value;  
   
                         for (int i = 0 ; i < ctrlTrigger->Triggers ; i++) {  
                             ::gig::MidiRuleCtrlTrigger::trigger_t* pTrigger =  
                                   &ctrlTrigger->pTriggers[i];  
   
                             // check if the controller has passed the  
                             // trigger point in the right direction  
                             if ((pTrigger->Descending &&  
                                  oldCCValue > pTrigger->TriggerPoint &&  
                                  newCCValue <= pTrigger->TriggerPoint) ||  
                                 (!pTrigger->Descending &&  
                                  oldCCValue < pTrigger->TriggerPoint &&  
                                  newCCValue >= pTrigger->TriggerPoint)) {  
   
                                 RTList<Event>::Iterator itNewEvent = pGlobalEvents->allocAppend();  
                                 if (itNewEvent) {  
                                     *itNewEvent = *itControlChangeEvent;  
                                     itNewEvent->Param.Note.Key = pTrigger->Key;  
   
                                     if (pTrigger->NoteOff || pTrigger->Velocity == 0) {  
                                         itNewEvent->Type = Event::type_note_off;  
                                         itNewEvent->Param.Note.Velocity = 100;  
   
                                         ProcessNoteOff(pEngineChannel, itNewEvent);  
                                     } else {  
                                         itNewEvent->Type = Event::type_note_on;  
                                         //TODO: if Velocity is 255, the triggered velocity should  
                                         // depend on how fast the controller is moving  
                                         itNewEvent->Param.Note.Velocity =  
                                             pTrigger->Velocity == 255 ? 100 :  
                                             pTrigger->Velocity;  
   
                                         ProcessNoteOn(pEngineChannel, itNewEvent);  
                                     }  
                                 }  
                                 else dmsg(1,("Event pool emtpy!\n"));  
                             }  
                         }  
                     }  
                 }  
             }  
         }  
   
         // update controller value in the engine channel's controller table  
         pEngineChannel->ControllerTable[itControlChangeEvent->Param.CC.Controller] = itControlChangeEvent->Param.CC.Value;  
   
         // handle hard coded MIDI controllers  
         switch (itControlChangeEvent->Param.CC.Controller) {  
             case 5: { // portamento time  
                 pEngineChannel->PortamentoTime = (float) itControlChangeEvent->Param.CC.Value / 127.0f * (float) CONFIG_PORTAMENTO_TIME_MAX + (float) CONFIG_PORTAMENTO_TIME_MIN;  
                 break;  
             }  
             case 6: { // data entry (currently only used for RPN controllers)  
                 if (pEngineChannel->GetMidiRpnController() == 2) { // coarse tuning in half tones  
                     int transpose = (int) itControlChangeEvent->Param.CC.Value - 64;  
                     // limit to +- two octaves for now  
                     transpose = RTMath::Min(transpose,  24);  
                     transpose = RTMath::Max(transpose, -24);  
                     pEngineChannel->GlobalTranspose = transpose;  
                     // workaround, so we won't have hanging notes  
                     ReleaseAllVoices(pEngineChannel, itControlChangeEvent);  
                 }  
                 // to avoid other MIDI CC #6 messages to be misenterpreted as RPN controller data  
                 pEngineChannel->ResetMidiRpnController();  
                 break;  
             }  
             case 7: { // volume  
                 //TODO: not sample accurate yet  
                 pEngineChannel->MidiVolume = VolumeCurve[itControlChangeEvent->Param.CC.Value];  
                 pEngineChannel->bStatusChanged = true; // engine channel status has changed, so set notify flag  
                 break;  
             }  
             case 10: { // panpot  
                 //TODO: not sample accurate yet  
                 pEngineChannel->GlobalPanLeft  = PanCurve[128 - itControlChangeEvent->Param.CC.Value];  
                 pEngineChannel->GlobalPanRight = PanCurve[itControlChangeEvent->Param.CC.Value];  
                 pEngineChannel->iLastPanRequest = itControlChangeEvent->Param.CC.Value;  
                 break;  
             }  
             case 64: { // sustain  
                 if (itControlChangeEvent->Param.CC.Value >= 64 && !pEngineChannel->SustainPedal) {  
                     dmsg(4,("DAMPER (RIGHT) PEDAL DOWN\n"));  
                     pEngineChannel->SustainPedal = true;  
   
                     #if !CONFIG_PROCESS_MUTED_CHANNELS  
                     if (pEngineChannel->GetMute()) return; // skip if sampler channel is muted  
                     #endif  
   
                     // cancel release process of voices if necessary  
                     RTList<uint>::Iterator iuiKey = pEngineChannel->pActiveKeys->first();  
                     for (; iuiKey; ++iuiKey) {  
                         midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[*iuiKey];  
                         if (!pKey->KeyPressed) {  
                             RTList<Event>::Iterator itNewEvent = pKey->pEvents->allocAppend();  
                             if (itNewEvent) {  
                                 *itNewEvent = *itControlChangeEvent; // 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) {  
                     dmsg(4,("DAMPER (RIGHT) PEDAL UP\n"));  
                     pEngineChannel->SustainPedal = false;  
   
                     #if !CONFIG_PROCESS_MUTED_CHANNELS  
                     if (pEngineChannel->GetMute()) return; // skip if sampler channel is muted  
                     #endif  
   
                     // release voices if their respective key is not pressed  
                     RTList<uint>::Iterator iuiKey = pEngineChannel->pActiveKeys->first();  
                     for (; iuiKey; ++iuiKey) {  
                         midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[*iuiKey];  
                         if (!pKey->KeyPressed && ShouldReleaseVoice(pEngineChannel, *iuiKey)) {  
                             RTList<Event>::Iterator itNewEvent = pKey->pEvents->allocAppend();  
                             if (itNewEvent) {  
                                 *itNewEvent = *itControlChangeEvent; // 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;  
             }  
             case 65: { // portamento on / off  
                 const bool bPortamento = itControlChangeEvent->Param.CC.Value >= 64;  
                 if (bPortamento != pEngineChannel->PortamentoMode)  
                     KillAllVoices(pEngineChannel, itControlChangeEvent);  
                 pEngineChannel->PortamentoMode = bPortamento;  
                 break;  
             }  
             case 66: { // sostenuto  
                 if (itControlChangeEvent->Param.CC.Value >= 64 && !pEngineChannel->SostenutoPedal) {  
                     dmsg(4,("SOSTENUTO (CENTER) PEDAL DOWN\n"));  
                     pEngineChannel->SostenutoPedal = true;  
   
                     #if !CONFIG_PROCESS_MUTED_CHANNELS  
                     if (pEngineChannel->GetMute()) return; // skip if sampler channel is muted  
                     #endif  
   
                     SostenutoKeyCount = 0;  
                     // Remeber the pressed keys  
                     RTList<uint>::Iterator iuiKey = pEngineChannel->pActiveKeys->first();  
                     for (; iuiKey; ++iuiKey) {  
                         midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[*iuiKey];  
                         if (pKey->KeyPressed && SostenutoKeyCount < 128) SostenutoKeys[SostenutoKeyCount++] = *iuiKey;  
                     }  
                 }  
                 if (itControlChangeEvent->Param.CC.Value < 64 && pEngineChannel->SostenutoPedal) {  
                     dmsg(4,("SOSTENUTO (CENTER) PEDAL UP\n"));  
                     pEngineChannel->SostenutoPedal = false;  
   
                     #if !CONFIG_PROCESS_MUTED_CHANNELS  
                     if (pEngineChannel->GetMute()) return; // skip if sampler channel is muted  
                     #endif  
   
                     // release voices if the damper pedal is up and their respective key is not pressed  
                     for (int i = 0; i < SostenutoKeyCount; i++) {  
                         midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[SostenutoKeys[i]];  
                         if (!pKey->KeyPressed && !pEngineChannel->SustainPedal) {  
                             RTList<Event>::Iterator itNewEvent = pKey->pEvents->allocAppend();  
                             if (itNewEvent) {  
                                 *itNewEvent = *itControlChangeEvent; // 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;  
             }  
             case 100: { // RPN controller LSB  
                 pEngineChannel->SetMidiRpnControllerLsb(itControlChangeEvent->Param.CC.Value);  
                 break;  
             }  
             case 101: { // RPN controller MSB  
                 pEngineChannel->SetMidiRpnControllerMsb(itControlChangeEvent->Param.CC.Value);  
                 break;  
             }  
   
   
             // Channel Mode Messages  
   
             case 120: { // all sound off  
                 KillAllVoices(pEngineChannel, itControlChangeEvent);  
                 break;  
             }  
             case 121: { // reset all controllers  
                 pEngineChannel->ResetControllers();  
                 break;  
             }  
             case 123: { // all notes off  
                 #if CONFIG_PROCESS_ALL_NOTES_OFF  
                 ReleaseAllVoices(pEngineChannel, itControlChangeEvent);  
                 #endif // CONFIG_PROCESS_ALL_NOTES_OFF  
                 break;  
             }  
             case 126: { // mono mode on  
                 if (!pEngineChannel->SoloMode)  
                     KillAllVoices(pEngineChannel, itControlChangeEvent);  
                 pEngineChannel->SoloMode = true;  
                 break;  
             }  
             case 127: { // poly mode on  
                 if (pEngineChannel->SoloMode)  
                     KillAllVoices(pEngineChannel, itControlChangeEvent);  
                 pEngineChannel->SoloMode = false;  
                 break;  
             }  
         }  
   
         // handle FX send controllers  
         if (!pEngineChannel->fxSends.empty()) {  
             for (int iFxSend = 0; iFxSend < pEngineChannel->GetFxSendCount(); iFxSend++) {  
                 FxSend* pFxSend = pEngineChannel->GetFxSend(iFxSend);  
                 if (pFxSend->MidiController() == itControlChangeEvent->Param.CC.Controller) {  
                     pFxSend->SetLevel(itControlChangeEvent->Param.CC.Value);  
                     pFxSend->SetInfoChanged(true);  
                 }  
             }  
         }  
     }  
   
     /**  
      *  Reacts on MIDI system exclusive messages.  
      *  
      *  @param itSysexEvent - sysex data size and time stamp of the sysex event  
      */  
     void Engine::ProcessSysex(Pool<Event>::Iterator& itSysexEvent) {  
         RingBuffer<uint8_t,false>::NonVolatileReader reader = pSysexBuffer->get_non_volatile_reader();  
   
         uint8_t exclusive_status, id;  
         if (!reader.pop(&exclusive_status)) goto free_sysex_data;  
         if (!reader.pop(&id))               goto free_sysex_data;  
         if (exclusive_status != 0xF0)       goto free_sysex_data;  
   
         switch (id) {  
             case 0x7f: { // (Realtime) Universal Sysex (GM Standard)  
                 uint8_t sysex_channel, sub_id1, sub_id2, val_msb, val_lsb;;  
                 if (!reader.pop(&sysex_channel)) goto free_sysex_data;  
                 if (!reader.pop(&sub_id1)) goto free_sysex_data;  
                 if (!reader.pop(&sub_id2)) goto free_sysex_data;  
                 if (!reader.pop(&val_lsb)) goto free_sysex_data;  
                 if (!reader.pop(&val_msb)) goto free_sysex_data;  
                 //TODO: for now we simply ignore the sysex channel, seldom used anyway  
                 switch (sub_id1) {  
                     case 0x04: // Device Control  
                         switch (sub_id2) {  
                             case 0x01: { // Master Volume  
                                 const double volume =  
                                     double((uint(val_msb)<<7) | uint(val_lsb)) / 16383.0;  
                                 #if CONFIG_MASTER_VOLUME_SYSEX_BY_PORT  
                                 // apply volume to all sampler channels that  
                                 // are connected to the same MIDI input port  
                                 // this sysex message arrived on  
                                 for (int i = 0; i < engineChannels.size(); ++i) {  
                                     EngineChannel* pEngineChannel = engineChannels[i];  
                                     if (pEngineChannel->GetMidiInputPort() ==  
                                         itSysexEvent->pMidiInputPort)  
                                     {  
                                         pEngineChannel->Volume(volume);  
                                     }  
                                 }  
                                 #else  
                                 // apply volume globally to the whole sampler  
                                 GLOBAL_VOLUME = volume;  
                                 #endif // CONFIG_MASTER_VOLUME_SYSEX_BY_PORT  
                                 break;  
                             }  
                         }  
                         break;  
                 }  
                 break;  
             }  
             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;  
                 if (!reader.pop(&cmd_id))    goto free_sysex_data;  
                 if (model_id != 0x42 /*GS*/) goto free_sysex_data;  
                 if (cmd_id != 0x12 /*DT1*/)  goto free_sysex_data;  
   
                 // command address  
                 uint8_t addr[3]; // 2 byte addr MSB, followed by 1 byte addr LSB)  
                 const RingBuffer<uint8_t,false>::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"));  
                     if (addr[2] == 0x7f) { // GS reset  
                         for (int i = 0; i < engineChannels.size(); ++i) {  
                             EngineChannel* pEngineChannel = engineChannels[i];  
                             if (pEngineChannel->GetMidiInputPort() == itSysexEvent->pMidiInputPort) {  
                                 KillAllVoices(pEngineChannel, itSysexEvent);  
                                 pEngineChannel->ResetControllers();  
                             }  
                         }  
                     }  
                 }  
                 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)  
                     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 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;  
                         }  
                         case 0x15: { // chromatic / drumkit mode  
                             dmsg(3,("\t\tMIDI Instrument Map Switch\n"));  
                             uint8_t part = addr[1] & 0x0f;  
                             uint8_t map;  
                             if (!reader.pop(&map)) goto free_sysex_data;  
                             for (int i = 0; i < engineChannels.size(); ++i) {  
                                 EngineChannel* pEngineChannel = engineChannels[i];  
                                 if (  
                                     (pEngineChannel->midiChannel == part ||  
                                      pEngineChannel->midiChannel == midi_chan_all) &&  
                                      pEngineChannel->GetMidiInputPort() == itSysexEvent->pMidiInputPort  
                                 ) {  
                                     try {  
                                         pEngineChannel->SetMidiInstrumentMap(map);  
                                     } catch (Exception e) {  
                                         dmsg(2,("\t\t\tCould not apply MIDI instrument map %d to part %d: %s\n", map, part, e.Message().c_str()));  
                                         goto free_sysex_data;  
                                     } catch (...) {  
                                         dmsg(2,("\t\t\tCould not apply MIDI instrument map %d to part %d (unknown exception)\n", map, part));  
                                         goto free_sysex_data;  
                                     }  
                                 }  
                             }  
                             dmsg(3,("\t\t\tApplied MIDI instrument map %d to part %d.\n", map, part));  
                             break;  
                         }  
                     }  
                 }  
                 else if (addr[0] == 0x40 && (addr[1] & 0xf0) == 0x20) { // Part Parameters (2)  
                 }  
                 else if (addr[0] == 0x41) { // Drum Setup Parameters  
                 }  
                 break;  
             }  
         }  
   
         free_sysex_data: // finally free sysex data  
         pSysexBuffer->increment_read_ptr(itSysexEvent->Param.Sysex.Size);  
     }  
   
     /**  
      * Calculates the Roland GS sysex check sum.  
      *  
      * @param AddrReader - reader which currently points to the first GS  
      *                     command address byte of the GS sysex message in  
      *                     question  
      * @param DataSize   - size of the GS message data (in bytes)  
      */  
     uint8_t Engine::GSCheckSum(const RingBuffer<uint8_t,false>::NonVolatileReader AddrReader, uint DataSize) {  
         RingBuffer<uint8_t,false>::NonVolatileReader reader = AddrReader;  
         uint bytes = 3 /*addr*/ + DataSize;  
         uint8_t addr_and_data[bytes];  
         reader.read(&addr_and_data[0], bytes);  
         uint8_t sum = 0;  
         for (uint i = 0; i < bytes; i++) sum += addr_and_data[i];  
         return 128 - sum % 128;  
     }  
   
     /**  
      * Allows to tune each of the twelve semitones of an octave.  
      *  
      * @param ScaleTunes - detuning of all twelve semitones (in cents)  
      */  
     void Engine::AdjustScale(int8_t ScaleTunes[12]) {  
         memcpy(&this->ScaleTuning[0], &ScaleTunes[0], 12); //TODO: currently not sample accurate  
     }  
   
     /**  
      * 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<Event>::Iterator& itReleaseEvent) {  
         RTList<uint>::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<Event>::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<Event>::Iterator& itKillEvent) {  
         RTList<uint>::Iterator iuiKey = pEngineChannel->pActiveKeys->first();  
         RTList<uint>::Iterator end    = pEngineChannel->pActiveKeys->end();  
         while (iuiKey != end) { // iterate through all active keys  
             midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[*iuiKey];  
             ++iuiKey;  
             RTList<Voice>::Iterator itVoice     = pKey->pActiveVoices->first();  
             RTList<Voice>::Iterator itVoicesEnd = pKey->pActiveVoices->end();  
             for (; itVoice != itVoicesEnd; ++itVoice) { // iterate through all voices on this key  
                 itVoice->Kill(itKillEvent);  
                 --VoiceSpawnsLeft; //FIXME: just a temporary workaround, we should check the cause in StealVoice() instead  
             }  
         }  
     }  
   
     /**  
      * Determines whether the specified voice should be released.  
      *  
      * @param pEngineChannel - The engine channel on which the voice should be checked  
      * @param Key - The key number  
      * @returns true if the specified should be released, false otherwise.  
      */  
     bool Engine::ShouldReleaseVoice(EngineChannel* pEngineChannel, int Key) {  
         if (pEngineChannel->SustainPedal) return false;  
   
         if (pEngineChannel->SostenutoPedal) {  
             for (int i = 0; i < SostenutoKeyCount; i++)  
                 if (Key == SostenutoKeys[i]) return false;  
         }  
   
         return true;  
     }  
   
     uint Engine::VoiceCount() {  
         return atomic_read(&ActiveVoiceCount);  
     }  
   
     void Engine::SetVoiceCount(uint Count) {  
         atomic_set(&ActiveVoiceCount, Count);  
     }  
   
     uint Engine::VoiceCountMax() {  
         return ActiveVoiceCountMax;  
     }  
   
     int Engine::MaxVoices() {  
         return pVoicePool->poolSize();  
     }  
   
     void Engine::SetMaxVoices(int iVoices) throw (Exception) {  
         if (iVoices < 1)  
             throw Exception("Maximum voices for an engine cannot be set lower than 1");  
   
         SuspendAll();  
   
         // NOTE: we need to clear pDimRegionsInUse before deleting pDimRegionPool,  
         // otherwise memory corruption will occur if there are active voices (see bug #118)  
         for (int iChannel = 0; iChannel < engineChannels.size(); iChannel++) {  
             engineChannels[iChannel]->ClearDimRegionsInUse();  
         }  
   
         if (pDimRegionPool[0]) delete pDimRegionPool[0];  
         if (pDimRegionPool[1]) delete pDimRegionPool[1];  
   
         pDimRegionPool[0] = new Pool< ::gig::DimensionRegion*>(iVoices);  
         pDimRegionPool[1] = new Pool< ::gig::DimensionRegion*>(iVoices);  
   
         for (int iChannel = 0; iChannel < engineChannels.size(); iChannel++) {  
             engineChannels[iChannel]->ResetDimRegionsInUse();  
         }  
   
         try {  
             pVoicePool->resizePool(iVoices);  
         } catch (...) {  
             throw Exception("FATAL: Could not resize voice pool!");  
         }  
   
         for (RTList<Voice>::Iterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) {  
             iterVoice->SetEngine(this);  
             iterVoice->pDiskThread = this->pDiskThread;  
         }  
         pVoicePool->clear();  
   
         ResumeAll();  
     }  
   
358      bool Engine::DiskStreamSupported() {      bool Engine::DiskStreamSupported() {
359          return true;          return true;
360      }      }
361    
     uint Engine::DiskStreamCount() {  
         return (pDiskThread) ? pDiskThread->GetActiveStreamCount() : 0;  
     }  
   
     uint Engine::DiskStreamCountMax() {  
         return (pDiskThread) ? pDiskThread->ActiveStreamCountMax : 0;  
     }  
   
     int Engine::MaxDiskStreams() {  
         return iMaxDiskStreams;  
     }  
   
     void Engine::SetMaxDiskStreams(int iStreams) throw (Exception) {  
         if (iStreams < 0)  
             throw Exception("Maximum disk streams for an engine cannot be set lower than 0");  
   
         SuspendAll();  
   
         iMaxDiskStreams = iStreams;  
   
         // reconnect to audio output device, because that will automatically  
         // recreate the disk thread with the required amount of streams  
         if (pAudioOutputDevice) Connect(pAudioOutputDevice);  
   
         ResumeAll();  
     }  
   
     String Engine::DiskStreamBufferFillBytes() {  
         return pDiskThread->GetBufferFillBytes();  
     }  
   
     String Engine::DiskStreamBufferFillPercentage() {  
         return pDiskThread->GetBufferFillPercentage();  
     }  
   
     String Engine::EngineName() {  
         return LS_GIG_ENGINE_NAME;  
     }  
   
362      String Engine::Description() {      String Engine::Description() {
363          return "Gigasampler Format Engine";          return "GigaSampler Format Engine";
364      }      }
365    
366      String Engine::Version() {      String Engine::Version() {
367          String s = "$Revision: 1.104 $";          String s = "$Revision: 1.105 $";
368          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
369      }      }
370    
     InstrumentManager* Engine::GetInstrumentManager() {  
         return &instruments;  
     }  
   
     // static constant initializers  
     const Engine::FloatTable Engine::VolumeCurve(InitVolumeCurve());  
     const Engine::FloatTable Engine::PanCurve(InitPanCurve());  
     const Engine::FloatTable Engine::CrossfadeCurve(InitCrossfadeCurve());  
   
     float* Engine::InitVolumeCurve() {  
         // line-segment approximation  
         const float segments[] = {  
             0, 0, 2, 0.0046, 16, 0.016, 31, 0.051, 45, 0.115, 54.5, 0.2,  
             64.5, 0.39, 74, 0.74, 92, 1.03, 114, 1.94, 119.2, 2.2, 127, 2.2  
         };  
         return InitCurve(segments);  
     }  
   
     float* Engine::InitPanCurve() {  
         // line-segment approximation  
         const float segments[] = {  
             0, 0, 1, 0,  
             2, 0.05, 31.5, 0.7, 51, 0.851, 74.5, 1.12,  
             127, 1.41, 128, 1.41  
         };  
         return InitCurve(segments, 129);  
     }  
   
     float* Engine::InitCrossfadeCurve() {  
         // line-segment approximation  
         const float segments[] = {  
             0, 0, 1, 0.03, 10, 0.1, 51, 0.58, 127, 1  
         };  
         return InitCurve(segments);  
     }  
   
     float* Engine::InitCurve(const float* segments, int size) {  
         float* y = new float[size];  
         for (int x = 0 ; x < size ; x++) {  
             if (x > segments[2]) segments += 2;  
             y[x] = segments[1] + (x - segments[0]) *  
                 (segments[3] - segments[1]) / (segments[2] - segments[0]);  
         }  
         return y;  
     }  
   
371  }} // namespace LinuxSampler::gig  }} // namespace LinuxSampler::gig

Legend:
Removed from v.2011  
changed lines
  Added in v.2012

  ViewVC Help
Powered by ViewVC