/[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 924 by schoenebeck, Sat Oct 21 14:13:09 2006 UTC revision 1751 by schoenebeck, Mon Jul 28 07:36:35 2008 UTC
# Line 2  Line 2 
2   *                                                                         *   *                                                                         *
3   *   LinuxSampler - modular, streaming capable sampler                     *   *   LinuxSampler - modular, streaming capable sampler                     *
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, 2006 Christian Schoenebeck                        *   *   Copyright (C) 2005-2008 Christian Schoenebeck                         *
7   *                                                                         *   *                                                                         *
8   *   This program is free software; you can redistribute it and/or modify  *   *   This program is free software; you can redistribute it and/or modify  *
9   *   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 29  Line 29 
29    
30  #include "Engine.h"  #include "Engine.h"
31    
32    #include "../../common/global_private.h"
33    
34  namespace LinuxSampler { namespace gig {  namespace LinuxSampler { namespace gig {
35    
36      InstrumentResourceManager Engine::instruments;      InstrumentResourceManager Engine::instruments;
# Line 51  namespace LinuxSampler { namespace gig { Line 53  namespace LinuxSampler { namespace gig {
53          if (engines.count(pDevice)) {          if (engines.count(pDevice)) {
54              dmsg(4,("Using existing gig::Engine.\n"));              dmsg(4,("Using existing gig::Engine.\n"));
55              pEngine = engines[pDevice];              pEngine = engines[pDevice];
56    
57                // Disable the engine while the new engine channel is
58                // added and initialized. The engine will be enabled again
59                // in EngineChannel::Connect.
60                pEngine->DisableAndLock();
61          } else { // create a new engine (and disk thread) instance for the given audio output device          } else { // create a new engine (and disk thread) instance for the given audio output device
62              dmsg(4,("Creating new gig::Engine.\n"));              dmsg(4,("Creating new gig::Engine.\n"));
63              pEngine = (Engine*) EngineFactory::Create("gig");              pEngine = (Engine*) EngineFactory::Create("gig");
# Line 67  namespace LinuxSampler { namespace gig { Line 74  namespace LinuxSampler { namespace gig {
74    
75      /**      /**
76       * Once an engine channel is disconnected from an audio output device,       * Once an engine channel is disconnected from an audio output device,
77       * it wil immediately call this method to unregister itself from the       * it will immediately call this method to unregister itself from the
78       * engine instance and if that engine instance is not used by any other       * engine instance and if that engine instance is not used by any other
79       * engine channel anymore, then that engine instance will be destroyed.       * engine channel anymore, then that engine instance will be destroyed.
80       *       *
# Line 93  namespace LinuxSampler { namespace gig { Line 100  namespace LinuxSampler { namespace gig {
100      /**      /**
101       * Constructor       * Constructor
102       */       */
103      Engine::Engine() {      Engine::Engine() : SuspendedRegions(128) {
104          pAudioOutputDevice = NULL;          pAudioOutputDevice = NULL;
105          pDiskThread        = NULL;          pDiskThread        = NULL;
106          pEventGenerator    = NULL;          pEventGenerator    = NULL;
107          pSysexBuffer       = new RingBuffer<uint8_t>(CONFIG_SYSEX_BUFFER_SIZE, 0);          pSysexBuffer       = new RingBuffer<uint8_t,false>(CONFIG_SYSEX_BUFFER_SIZE, 0);
108          pEventQueue        = new RingBuffer<Event>(CONFIG_MAX_EVENTS_PER_FRAGMENT, 0);          pEventQueue        = new RingBuffer<Event,false>(CONFIG_MAX_EVENTS_PER_FRAGMENT, 0);
109          pEventPool         = new Pool<Event>(CONFIG_MAX_EVENTS_PER_FRAGMENT);          pEventPool         = new Pool<Event>(CONFIG_MAX_EVENTS_PER_FRAGMENT);
110          pVoicePool         = new Pool<Voice>(CONFIG_MAX_VOICES);          pVoicePool         = new Pool<Voice>(CONFIG_MAX_VOICES);
111            pDimRegionPool[0]  = new Pool< ::gig::DimensionRegion*>(CONFIG_MAX_VOICES);
112            pDimRegionPool[1]  = new Pool< ::gig::DimensionRegion*>(CONFIG_MAX_VOICES);
113          pVoiceStealingQueue = new RTList<Event>(pEventPool);          pVoiceStealingQueue = new RTList<Event>(pEventPool);
114          pGlobalEvents      = new RTList<Event>(pEventPool);          pGlobalEvents      = new RTList<Event>(pEventPool);
115    
116          for (RTList<Voice>::Iterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) {          for (RTList<Voice>::Iterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) {
117              iterVoice->SetEngine(this);              iterVoice->SetEngine(this);
118          }          }
# Line 110  namespace LinuxSampler { namespace gig { Line 120  namespace LinuxSampler { namespace gig {
120    
121          ResetInternal();          ResetInternal();
122          ResetScaleTuning();          ResetScaleTuning();
123            ResetSuspendedRegions();
124      }      }
125    
126      /**      /**
# Line 132  namespace LinuxSampler { namespace gig { Line 143  namespace LinuxSampler { namespace gig {
143          if (pEventGenerator) delete pEventGenerator;          if (pEventGenerator) delete pEventGenerator;
144          if (pVoiceStealingQueue) delete pVoiceStealingQueue;          if (pVoiceStealingQueue) delete pVoiceStealingQueue;
145          if (pSysexBuffer) delete pSysexBuffer;          if (pSysexBuffer) delete pSysexBuffer;
146          EngineFactory::Destroy(this);          if (pGlobalEvents) delete pGlobalEvents;
147            if (pDimRegionPool[0]) delete pDimRegionPool[0];
148            if (pDimRegionPool[1]) delete pDimRegionPool[1];
149            ResetSuspendedRegions();
150            Unregister();
151      }      }
152    
153      void Engine::Enable() {      void Engine::Enable() {
# Line 141  namespace LinuxSampler { namespace gig { Line 156  namespace LinuxSampler { namespace gig {
156          dmsg(3,("gig::Engine: enabled (val=%d)\n", EngineDisabled.GetUnsafe()));          dmsg(3,("gig::Engine: enabled (val=%d)\n", EngineDisabled.GetUnsafe()));
157      }      }
158    
159        /**
160         * Temporarily stop the engine to not do anything. The engine will just be
161         * frozen during that time, that means after enabling it again it will
162         * continue where it was, with all its voices and playback state it had at
163         * the point of disabling. Notice that the engine's (audio) thread will
164         * continue to run, it just remains in an inactive loop during that time.
165         *
166         * If you need to be sure that all voices and disk streams are killed as
167         * well, use @c SuspendAll() instead.
168         *
169         * @see Enable(), SuspendAll()
170         */
171      void Engine::Disable() {      void Engine::Disable() {
172          dmsg(3,("gig::Engine: disabling\n"));          dmsg(3,("gig::Engine: disabling\n"));
173          bool* pWasDisabled = EngineDisabled.PushAndUnlock(true, 2); // wait max. 2s          bool* pWasDisabled = EngineDisabled.PushAndUnlock(true, 2); // wait max. 2s
# Line 154  namespace LinuxSampler { namespace gig { Line 181  namespace LinuxSampler { namespace gig {
181      }      }
182    
183      /**      /**
184         * Similar to @c Disable() but this method additionally kills all voices
185         * and disk streams and blocks until all voices and disk streams are actually
186         * killed / deleted.
187         *
188         * @e Note: only the original calling thread is able to re-enable the
189         * engine afterwards by calling @c ResumeAll() later on!
190         */
191        void Engine::SuspendAll() {
192            dmsg(2,("gig::Engine: Suspending all ...\n"));
193            // stop the engine, so we can safely modify the engine's
194            // data structures from this foreign thread
195            DisableAndLock();
196            // we could also use the respective class member variable here,
197            // but this is probably safer and cleaner
198            int iPendingStreamDeletions = 0;
199            // kill all voices on all engine channels the *die hard* way
200            for (int iChannel = 0; iChannel < engineChannels.size(); iChannel++) {
201                EngineChannel* pEngineChannel = engineChannels[iChannel];
202                RTList<uint>::Iterator iuiKey = pEngineChannel->pActiveKeys->first();
203                RTList<uint>::Iterator end    = pEngineChannel->pActiveKeys->end();
204                for (; iuiKey != end; ++iuiKey) { // iterate through all active keys
205                    midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[*iuiKey];
206                    RTList<Voice>::Iterator itVoice = pKey->pActiveVoices->first();
207                    RTList<Voice>::Iterator itVoicesEnd = pKey->pActiveVoices->end();
208                    for (; itVoice != itVoicesEnd; ++itVoice) { // iterate through all voices on this key
209                        // request a notification from disk thread side for stream deletion
210                        const Stream::Handle hStream = itVoice->KillImmediately(true);
211                        if (hStream != Stream::INVALID_HANDLE) { // voice actually used a stream
212                            iPendingStreamDeletions++;
213                        }
214                    }
215                }
216            }
217            // wait until all streams were actually deleted by the disk thread
218            while (iPendingStreamDeletions) {
219                while (
220                    iPendingStreamDeletions &&
221                    pDiskThread->AskForDeletedStream() != Stream::INVALID_HANDLE
222                ) iPendingStreamDeletions--;
223                if (!iPendingStreamDeletions) break;
224                usleep(10000); // sleep for 10ms
225            }
226            dmsg(2,("gig::Engine: Everything suspended.\n"));
227        }
228    
229        /**
230         * At the moment same as calling @c Enable() directly, but this might
231         * change in future, so better call this method as counterpart to
232         * @c SuspendAll() instead of @c Enable() !
233         */
234        void Engine::ResumeAll() {
235            Enable();
236        }
237    
238        /**
239         * Order the engine to stop rendering audio for the given region.
240         * Additionally this method will block until all voices and their disk
241         * streams associated with that region are actually killed / deleted, so
242         * one can i.e. safely modify the region with an instrument editor after
243         * returning from this method.
244         *
245         * @param pRegion - region the engine shall stop using
246         */
247        void Engine::Suspend(::gig::Region* pRegion) {
248            dmsg(2,("gig::Engine: Suspending Region %x ...\n",pRegion));
249            SuspendedRegionsMutex.Lock();
250            SuspensionChangeOngoing.Set(true);
251            pPendingRegionSuspension = pRegion;
252            SuspensionChangeOngoing.WaitAndUnlockIf(true);
253            SuspendedRegionsMutex.Unlock();
254            dmsg(2,("gig::Engine: Region %x suspended.",pRegion));
255        }
256    
257        /**
258         * Orders the engine to resume playing back the given region, previously
259         * suspended with @c Suspend() .
260         *
261         * @param pRegion - region the engine shall be allowed to use again
262         */
263        void Engine::Resume(::gig::Region* pRegion) {
264            dmsg(2,("gig::Engine: Resuming Region %x ...\n",pRegion));
265            SuspendedRegionsMutex.Lock();
266            SuspensionChangeOngoing.Set(true);
267            pPendingRegionResumption = pRegion;
268            SuspensionChangeOngoing.WaitAndUnlockIf(true);
269            SuspendedRegionsMutex.Unlock();
270            dmsg(2,("gig::Engine: Region %x resumed.\n",pRegion));
271        }
272    
273        /**
274       *  Reset all voices and disk thread and clear input event queue and all       *  Reset all voices and disk thread and clear input event queue and all
275       *  control and status variables.       *  control and status variables.
276       */       */
# Line 208  namespace LinuxSampler { namespace gig { Line 325  namespace LinuxSampler { namespace gig {
325          memset(&ScaleTuning[0], 0x00, 12);          memset(&ScaleTuning[0], 0x00, 12);
326      }      }
327    
328        void Engine::ResetSuspendedRegions() {
329            SuspendedRegions.clear();
330            iPendingStreamDeletions = 0;
331            pPendingRegionSuspension = pPendingRegionResumption = NULL;
332            SuspensionChangeOngoing.Set(false);
333        }
334    
335      /**      /**
336       * Connect this engine instance with the given audio output device.       * Connect this engine instance with the given audio output device.
337       * This method will be called when an Engine instance is created.       * This method will be called when an Engine instance is created.
# Line 234  namespace LinuxSampler { namespace gig { Line 358  namespace LinuxSampler { namespace gig {
358          this->MaxSamplesPerCycle = pAudioOutputDevice->MaxSamplesPerCycle();          this->MaxSamplesPerCycle = pAudioOutputDevice->MaxSamplesPerCycle();
359          this->SampleRate         = pAudioOutputDevice->SampleRate();          this->SampleRate         = pAudioOutputDevice->SampleRate();
360    
361          // FIXME: audio drivers with varying fragment sizes might be a problem here          MinFadeOutSamples = int(double(SampleRate) * CONFIG_EG_MIN_RELEASE_TIME) - 1;
362          MaxFadeOutPos = MaxSamplesPerCycle - int(double(SampleRate) * CONFIG_EG_MIN_RELEASE_TIME) - 1;          if (MaxSamplesPerCycle < MinFadeOutSamples) {
         if (MaxFadeOutPos < 0) {  
363              std::cerr << "gig::Engine: WARNING, CONFIG_EG_MIN_RELEASE_TIME "              std::cerr << "gig::Engine: WARNING, CONFIG_EG_MIN_RELEASE_TIME "
364                        << "too big for current audio fragment size & sampling rate! "                        << "too big for current audio fragment size & sampling rate! "
365                        << "May lead to click sounds if voice stealing chimes in!\n" << std::flush;                        << "May lead to click sounds if voice stealing chimes in!\n" << std::flush;
366              // force volume ramp downs at the beginning of each fragment              // force volume ramp downs at the beginning of each fragment
367              MaxFadeOutPos = 0;              MinFadeOutSamples = MaxSamplesPerCycle;
368              // lower minimum release time              // lower minimum release time
369              const float minReleaseTime = (float) MaxSamplesPerCycle / (float) SampleRate;              const float minReleaseTime = (float) MaxSamplesPerCycle / (float) SampleRate;
370              for (RTList<Voice>::Iterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) {              for (RTList<Voice>::Iterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) {
# Line 257  namespace LinuxSampler { namespace gig { Line 380  namespace LinuxSampler { namespace gig {
380              delete this->pDiskThread;              delete this->pDiskThread;
381              dmsg(1,("OK\n"));              dmsg(1,("OK\n"));
382          }          }
383          this->pDiskThread = new DiskThread(((pAudioOut->MaxSamplesPerCycle() << CONFIG_MAX_PITCH) << 1) + 6); //FIXME: assuming stereo          this->pDiskThread = new DiskThread(((pAudioOut->MaxSamplesPerCycle() << CONFIG_MAX_PITCH) << 1) + 6, //FIXME: assuming stereo
384                                               &instruments);
385          if (!pDiskThread) {          if (!pDiskThread) {
386              dmsg(0,("gig::Engine  new diskthread = NULL\n"));              dmsg(0,("gig::Engine  new diskthread = NULL\n"));
387              exit(EXIT_FAILURE);              exit(EXIT_FAILURE);
# Line 286  namespace LinuxSampler { namespace gig { Line 410  namespace LinuxSampler { namespace gig {
410      }      }
411    
412      /**      /**
413         * Called by the engine's (audio) thread once per cycle to process requests
414         * from the outer world to suspend or resume a given @c gig::Region .
415         */
416        void Engine::ProcessSuspensionsChanges() {
417            // process request for suspending one region
418            if (pPendingRegionSuspension) {
419                // kill all voices on all engine channels that use this region
420                for (int iChannel = 0; iChannel < engineChannels.size(); iChannel++) {
421                    EngineChannel* pEngineChannel = engineChannels[iChannel];
422                    RTList<uint>::Iterator iuiKey = pEngineChannel->pActiveKeys->first();
423                    RTList<uint>::Iterator end    = pEngineChannel->pActiveKeys->end();
424                    for (; iuiKey != end; ++iuiKey) { // iterate through all active keys
425                        midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[*iuiKey];
426                        RTList<Voice>::Iterator itVoice = pKey->pActiveVoices->first();
427                        // if current key is not associated with this region, skip this key
428                        if (itVoice->pDimRgn->GetParent() != pPendingRegionSuspension) continue;
429                        RTList<Voice>::Iterator itVoicesEnd = pKey->pActiveVoices->end();
430                        for (; itVoice != itVoicesEnd; ++itVoice) { // iterate through all voices on this key
431                            // request a notification from disk thread side for stream deletion
432                            const Stream::Handle hStream = itVoice->KillImmediately(true);
433                            if (hStream != Stream::INVALID_HANDLE) { // voice actually used a stream
434                                iPendingStreamDeletions++;
435                            }
436                        }
437                    }
438                }
439                // make sure the region is not yet on the list
440                bool bAlreadySuspended = false;
441                RTList< ::gig::Region*>::Iterator iter = SuspendedRegions.first();
442                RTList< ::gig::Region*>::Iterator end  = SuspendedRegions.end();
443                for (; iter != end; ++iter) { // iterate through all suspended regions
444                    if (*iter == pPendingRegionSuspension) { // found
445                        bAlreadySuspended = true;
446                        dmsg(1,("gig::Engine: attempt to suspend an already suspended region !!!\n"));
447                        break;
448                    }
449                }
450                if (!bAlreadySuspended) {
451                    // put the region on the list of suspended regions
452                    RTList< ::gig::Region*>::Iterator iter = SuspendedRegions.allocAppend();
453                    if (iter) {
454                        *iter = pPendingRegionSuspension;
455                    } else std::cerr << "gig::Engine: Could not suspend Region, list is full. This is a bug!!!\n" << std::flush;
456                }
457                // free request slot for next caller (and to make sure that
458                // we're not going to process the same request in the next cycle)
459                pPendingRegionSuspension = NULL;
460                // if no disk stream deletions are pending, awaken other side, as
461                // we're done in this case
462                if (!iPendingStreamDeletions) SuspensionChangeOngoing.Set(false);
463            }
464    
465            // process request for resuming one region
466            if (pPendingRegionResumption) {
467                // remove region from the list of suspended regions
468                RTList< ::gig::Region*>::Iterator iter = SuspendedRegions.first();
469                RTList< ::gig::Region*>::Iterator end  = SuspendedRegions.end();
470                for (; iter != end; ++iter) { // iterate through all suspended regions
471                    if (*iter == pPendingRegionResumption) { // found
472                        SuspendedRegions.free(iter);
473                        break; // done
474                    }
475                }
476                // free request slot for next caller
477                pPendingRegionResumption = NULL;
478                // awake other side as we're done
479                SuspensionChangeOngoing.Set(false);
480            }
481        }
482    
483        /**
484         * Called by the engine's (audio) thread once per cycle to check if
485         * streams of voices that were killed due to suspension request have
486         * finally really been deleted by the disk thread.
487         */
488        void Engine::ProcessPendingStreamDeletions() {
489            if (!iPendingStreamDeletions) return;
490            //TODO: or shall we better store a list with stream handles instead of a scalar amount of streams to be deleted? might be safer
491            while (
492                iPendingStreamDeletions &&
493                pDiskThread->AskForDeletedStream() != Stream::INVALID_HANDLE
494            ) iPendingStreamDeletions--;
495            // just for safety ...
496            while (pDiskThread->AskForDeletedStream() != Stream::INVALID_HANDLE);
497            // now that all disk streams are deleted, awake other side as
498            // we're finally done with suspending the requested region
499            if (!iPendingStreamDeletions) SuspensionChangeOngoing.Set(false);
500        }
501    
502        /**
503         * Returns @c true if the given region is currently set to be suspended
504         * from being used, @c false otherwise.
505         */
506        bool Engine::RegionSuspended(::gig::Region* pRegion) {
507            if (SuspendedRegions.isEmpty()) return false;
508            //TODO: or shall we use a sorted container instead of the RTList? might be faster ... or trivial ;-)
509            RTList< ::gig::Region*>::Iterator iter = SuspendedRegions.first();
510            RTList< ::gig::Region*>::Iterator end  = SuspendedRegions.end();
511            for (; iter != end; ++iter)  // iterate through all suspended regions
512                if (*iter == pRegion) return true;
513            return false;
514        }
515    
516        /**
517       * Clear all engine global event lists.       * Clear all engine global event lists.
518       */       */
519      void Engine::ClearEventLists() {      void Engine::ClearEventLists() {
# Line 306  namespace LinuxSampler { namespace gig { Line 534  namespace LinuxSampler { namespace gig {
534       *                  current audio cycle       *                  current audio cycle
535       */       */
536      void Engine::ImportEvents(uint Samples) {      void Engine::ImportEvents(uint Samples) {
537          RingBuffer<Event>::NonVolatileReader eventQueueReader = pEventQueue->get_non_volatile_reader();          RingBuffer<Event,false>::NonVolatileReader eventQueueReader = pEventQueue->get_non_volatile_reader();
538          Event* pEvent;          Event* pEvent;
539          while (true) {          while (true) {
540              // get next event from input event queue              // get next event from input event queue
# Line 340  namespace LinuxSampler { namespace gig { Line 568  namespace LinuxSampler { namespace gig {
568       *  @returns       0 on success       *  @returns       0 on success
569       */       */
570      int Engine::RenderAudio(uint Samples) {      int Engine::RenderAudio(uint Samples) {
571          dmsg(5,("RenderAudio(Samples=%d)\n", Samples));          dmsg(8,("RenderAudio(Samples=%d)\n", Samples));
572    
573          // return if engine disabled          // return if engine disabled
574          if (EngineDisabled.Pop()) {          if (EngineDisabled.Pop()) {
# Line 348  namespace LinuxSampler { namespace gig { Line 576  namespace LinuxSampler { namespace gig {
576              return 0;              return 0;
577          }          }
578    
579            // process requests for suspending / resuming regions (i.e. to avoid
580            // crashes while these regions are modified by an instrument editor)
581            ProcessSuspensionsChanges();
582    
583          // update time of start and end of this audio fragment (as events' time stamps relate to this)          // update time of start and end of this audio fragment (as events' time stamps relate to this)
584          pEventGenerator->UpdateFragmentTime(Samples);          pEventGenerator->UpdateFragmentTime(Samples);
585    
# Line 377  namespace LinuxSampler { namespace gig { Line 609  namespace LinuxSampler { namespace gig {
609          // reset internal voice counter (just for statistic of active voices)          // reset internal voice counter (just for statistic of active voices)
610          ActiveVoiceCountTemp = 0;          ActiveVoiceCountTemp = 0;
611    
612            // handle instrument change commands
613            bool instrumentChanged = false;
614            for (int i = 0; i < engineChannels.size(); i++) {
615                EngineChannel* pEngineChannel = engineChannels[i];
616    
617                // as we're going to (carefully) write some status to the
618                // synchronized struct, we cast away the const
619                EngineChannel::instrument_change_command_t& cmd =
620                    const_cast<EngineChannel::instrument_change_command_t&>(pEngineChannel->InstrumentChangeCommandReader.Lock());
621    
622                pEngineChannel->pDimRegionsInUse = cmd.pDimRegionsInUse;
623                pEngineChannel->pDimRegionsInUse->clear();
624    
625                if (cmd.bChangeInstrument) {
626                    // change instrument
627                    dmsg(5,("Engine: instrument change command received\n"));
628                    cmd.bChangeInstrument = false;
629                    pEngineChannel->pInstrument = cmd.pInstrument;
630                    instrumentChanged = true;
631    
632                    // Iterate through all active voices and mark them as
633                    // "orphans", which means that the dimension regions
634                    // and samples they use should be released to the
635                    // instrument resource manager when the voices die.
636                    int i = 0;
637                    RTList<uint>::Iterator iuiKey = pEngineChannel->pActiveKeys->first();
638                    RTList<uint>::Iterator end    = pEngineChannel->pActiveKeys->end();
639                    while (iuiKey != end) { // iterate through all active keys
640                        midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[*iuiKey];
641                        ++iuiKey;
642    
643                        RTList<Voice>::Iterator itVoice     = pKey->pActiveVoices->first();
644                        RTList<Voice>::Iterator itVoicesEnd = pKey->pActiveVoices->end();
645                        for (; itVoice != itVoicesEnd; ++itVoice) { // iterate through all voices on this key
646                            itVoice->Orphan = true;
647                        }
648                    }
649                }
650            }
651            if (instrumentChanged) {
652                //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
653                ResetSuspendedRegions();
654            }
655    
656          // handle events on all engine channels          // handle events on all engine channels
657          for (int i = 0; i < engineChannels.size(); i++) {          for (int i = 0; i < engineChannels.size(); i++) {
             if (!engineChannels[i]->pInstrument) continue; // ignore if no instrument loaded  
658              ProcessEvents(engineChannels[i], Samples);              ProcessEvents(engineChannels[i], Samples);
659          }          }
660    
661          // render all 'normal', active voices on all engine channels          // render all 'normal', active voices on all engine channels
662          for (int i = 0; i < engineChannels.size(); i++) {          for (int i = 0; i < engineChannels.size(); i++) {
             if (!engineChannels[i]->pInstrument) continue; // ignore if no instrument loaded  
663              RenderActiveVoices(engineChannels[i], Samples);              RenderActiveVoices(engineChannels[i], Samples);
664          }          }
665    
666          // now that all ordinary voices on ALL engine channels are rendered, render new stolen voices          // now that all ordinary voices on ALL engine channels are rendered, render new stolen voices
667          RenderStolenVoices(Samples);          RenderStolenVoices(Samples);
668    
669            // handle audio routing for engine channels with FX sends
670            for (int i = 0; i < engineChannels.size(); i++) {
671                if (engineChannels[i]->fxSends.empty()) continue; // ignore if no FX sends
672                RouteAudio(engineChannels[i], Samples);
673            }
674    
675          // handle cleanup on all engine channels for the next audio fragment          // handle cleanup on all engine channels for the next audio fragment
676          for (int i = 0; i < engineChannels.size(); i++) {          for (int i = 0; i < engineChannels.size(); i++) {
             if (!engineChannels[i]->pInstrument) continue; // ignore if no instrument loaded  
677              PostProcess(engineChannels[i]);              PostProcess(engineChannels[i]);
678          }          }
679    
# Line 409  namespace LinuxSampler { namespace gig { Line 688  namespace LinuxSampler { namespace gig {
688          ActiveVoiceCount = ActiveVoiceCountTemp;          ActiveVoiceCount = ActiveVoiceCountTemp;
689          if (ActiveVoiceCount > ActiveVoiceCountMax) ActiveVoiceCountMax = ActiveVoiceCount;          if (ActiveVoiceCount > ActiveVoiceCountMax) ActiveVoiceCountMax = ActiveVoiceCount;
690    
691            // in case regions were previously suspended and we killed voices
692            // with disk streams due to that, check if those streams have finally
693            // been deleted by the disk thread
694            if (iPendingStreamDeletions) ProcessPendingStreamDeletions();
695    
696            for (int i = 0; i < engineChannels.size(); i++) {
697                engineChannels[i]->InstrumentChangeCommandReader.Unlock();
698            }
699          FrameTime += Samples;          FrameTime += Samples;
700    
701          return 0;          return 0;
# Line 476  namespace LinuxSampler { namespace gig { Line 763  namespace LinuxSampler { namespace gig {
763          if (pEngineChannel->GetMute()) return; // skip if sampler channel is muted          if (pEngineChannel->GetMute()) return; // skip if sampler channel is muted
764          #endif          #endif
765    
766            uint voiceCount = 0;
767            uint streamCount = 0;
768          RTList<uint>::Iterator iuiKey = pEngineChannel->pActiveKeys->first();          RTList<uint>::Iterator iuiKey = pEngineChannel->pActiveKeys->first();
769          RTList<uint>::Iterator end    = pEngineChannel->pActiveKeys->end();          RTList<uint>::Iterator end    = pEngineChannel->pActiveKeys->end();
770          while (iuiKey != end) { // iterate through all active keys          while (iuiKey != end) { // iterate through all active keys
# Line 487  namespace LinuxSampler { namespace gig { Line 776  namespace LinuxSampler { namespace gig {
776              for (; itVoice != itVoicesEnd; ++itVoice) { // iterate through all voices on this key              for (; itVoice != itVoicesEnd; ++itVoice) { // iterate through all voices on this key
777                  // now render current voice                  // now render current voice
778                  itVoice->Render(Samples);                  itVoice->Render(Samples);
779                  if (itVoice->IsActive()) ActiveVoiceCountTemp++; // still active                  if (itVoice->IsActive()) { // still active
780                  else { // voice reached end, is now inactive                      if (!itVoice->Orphan) {
781                            *(pEngineChannel->pDimRegionsInUse->allocAppend()) = itVoice->pDimRgn;
782                        }
783                        ActiveVoiceCountTemp++;
784                        voiceCount++;
785    
786                        if (itVoice->PlaybackState == Voice::playback_state_disk) {
787                            if ((itVoice->DiskStreamRef).State == Stream::state_active) streamCount++;
788                        }
789                    }  else { // voice reached end, is now inactive
790                      FreeVoice(pEngineChannel, itVoice); // remove voice from the list of active voices                      FreeVoice(pEngineChannel, itVoice); // remove voice from the list of active voices
791                  }                  }
792              }              }
793          }          }
794    
795            pEngineChannel->SetVoiceCount(voiceCount);
796            pEngineChannel->SetDiskStreamCount(streamCount);
797      }      }
798    
799      /**      /**
# Line 513  namespace LinuxSampler { namespace gig { Line 814  namespace LinuxSampler { namespace gig {
814          RTList<Event>::Iterator end               = pVoiceStealingQueue->end();          RTList<Event>::Iterator end               = pVoiceStealingQueue->end();
815          for (; itVoiceStealEvent != end; ++itVoiceStealEvent) {          for (; itVoiceStealEvent != end; ++itVoiceStealEvent) {
816              EngineChannel* pEngineChannel = (EngineChannel*) itVoiceStealEvent->pEngineChannel;              EngineChannel* pEngineChannel = (EngineChannel*) itVoiceStealEvent->pEngineChannel;
817                if (!pEngineChannel->pInstrument) continue; // ignore if no instrument loaded
818              Pool<Voice>::Iterator itNewVoice =              Pool<Voice>::Iterator itNewVoice =
819                  LaunchVoice(pEngineChannel, itVoiceStealEvent, itVoiceStealEvent->Param.Note.Layer, itVoiceStealEvent->Param.Note.ReleaseTrigger, false, false);                  LaunchVoice(pEngineChannel, itVoiceStealEvent, itVoiceStealEvent->Param.Note.Layer, itVoiceStealEvent->Param.Note.ReleaseTrigger, false, false);
820              if (itNewVoice) {              if (itNewVoice) {
821                  itNewVoice->Render(Samples);                  itNewVoice->Render(Samples);
822                  if (itNewVoice->IsActive()) ActiveVoiceCountTemp++; // still active                  if (itNewVoice->IsActive()) { // still active
823                  else { // voice reached end, is now inactive                      *(pEngineChannel->pDimRegionsInUse->allocAppend()) = itNewVoice->pDimRgn;
824                        ActiveVoiceCountTemp++;
825                        pEngineChannel->SetVoiceCount(pEngineChannel->GetVoiceCount() + 1);
826    
827                        if (itNewVoice->PlaybackState == Voice::playback_state_disk) {
828                            if (itNewVoice->DiskStreamRef.State == Stream::state_active) {
829                                pEngineChannel->SetDiskStreamCount(pEngineChannel->GetDiskStreamCount() + 1);
830                            }
831                        }
832                    } else { // voice reached end, is now inactive
833                      FreeVoice(pEngineChannel, itNewVoice); // remove voice from the list of active voices                      FreeVoice(pEngineChannel, itNewVoice); // remove voice from the list of active voices
834                  }                  }
835              }              }
# Line 532  namespace LinuxSampler { namespace gig { Line 843  namespace LinuxSampler { namespace gig {
843      }      }
844    
845      /**      /**
846         * Will be called in case the respective engine channel sports FX send
847         * channels. In this particular case, engine channel local buffers are
848         * used to render and mix all voices to. This method is responsible for
849         * copying the audio data from those local buffers to the master audio
850         * output channels as well as to the FX send audio output channels with
851         * their respective FX send levels.
852         *
853         * @param pEngineChannel - engine channel from which audio should be
854         *                         routed
855         * @param Samples        - amount of sample points to be routed in
856         *                         this audio fragment cycle
857         */
858        void Engine::RouteAudio(EngineChannel* pEngineChannel, uint Samples) {
859            // route dry signal
860            {
861                AudioChannel* pDstL = pAudioOutputDevice->Channel(pEngineChannel->AudioDeviceChannelLeft);
862                AudioChannel* pDstR = pAudioOutputDevice->Channel(pEngineChannel->AudioDeviceChannelRight);
863                pEngineChannel->pChannelLeft->MixTo(pDstL, Samples);
864                pEngineChannel->pChannelRight->MixTo(pDstR, Samples);
865            }
866            // route FX send signal
867            {
868                for (int iFxSend = 0; iFxSend < pEngineChannel->GetFxSendCount(); iFxSend++) {
869                    FxSend* pFxSend = pEngineChannel->GetFxSend(iFxSend);
870                    for (int iChan = 0; iChan < 2; ++iChan) {
871                        AudioChannel* pSource =
872                            (iChan)
873                                ? pEngineChannel->pChannelRight
874                                : pEngineChannel->pChannelLeft;
875                        const int iDstChan = pFxSend->DestinationChannel(iChan);
876                        if (iDstChan < 0) {
877                            dmsg(1,("Engine::RouteAudio() Error: invalid FX send (%s) destination channel (%d->%d)", ((iChan) ? "R" : "L"), iChan, iDstChan));
878                            goto channel_cleanup;
879                        }
880                        AudioChannel* pDstChan = NULL;
881                        if (pFxSend->DestinationMasterEffectChain() >= 0) { // fx send routed to an internal master effect
882                            EffectChain* pEffectChain =
883                                pAudioOutputDevice->MasterEffectChain(
884                                    pFxSend->DestinationMasterEffectChain()
885                                );
886                            if (!pEffectChain) {
887                                dmsg(1,("Engine::RouteAudio() Error: invalid FX send (%s) destination effect chain %d", ((iChan) ? "R" : "L"), pFxSend->DestinationMasterEffectChain()));
888                                goto channel_cleanup;
889                            }
890                            Effect* pEffect =
891                                pEffectChain->GetEffect(
892                                    pFxSend->DestinationMasterEffect()
893                                );
894                            if (!pEffect) {
895                                dmsg(1,("Engine::RouteAudio() Error: invalid FX send (%s) destination effect %d of effect chain %d", ((iChan) ? "R" : "L"), pFxSend->DestinationMasterEffect(), pFxSend->DestinationMasterEffectChain()));
896                                goto channel_cleanup;
897                            }
898                            pDstChan = pEffect->InputChannel(iDstChan);
899                        } else { // FX send routed directly to an audio output channel
900                            pDstChan = pAudioOutputDevice->Channel(iDstChan);
901                        }
902                        if (!pDstChan) {
903                            dmsg(1,("Engine::RouteAudio() Error: invalid FX send (%s) destination channel (%d->%d)", ((iChan) ? "R" : "L"), iChan, iDstChan));
904                            goto channel_cleanup;
905                        }
906                        pSource->MixTo(pDstChan, Samples, pFxSend->Level());
907                    }
908                }
909            }
910            channel_cleanup:
911            // reset buffers with silence (zero out) for the next audio cycle
912            pEngineChannel->pChannelLeft->Clear();
913            pEngineChannel->pChannelRight->Clear();
914        }
915    
916        /**
917       * Free all keys which have turned inactive in this audio fragment, from       * Free all keys which have turned inactive in this audio fragment, from
918       * the list of active keys and clear all event lists on that engine       * the list of active keys and clear all event lists on that engine
919       * channel.       * channel.
# Line 571  namespace LinuxSampler { namespace gig { Line 953  namespace LinuxSampler { namespace gig {
953       *       *
954       *  @param pData - pointer to sysex data       *  @param pData - pointer to sysex data
955       *  @param Size  - lenght of sysex data (in bytes)       *  @param Size  - lenght of sysex data (in bytes)
956         *  @param pSender - the MIDI input port on which the SysEx message was
957         *                   received
958       */       */
959      void Engine::SendSysex(void* pData, uint Size) {      void Engine::SendSysex(void* pData, uint Size, MidiInputPort* pSender) {
960          Event event             = pEventGenerator->CreateEvent();          Event event             = pEventGenerator->CreateEvent();
961          event.Type              = Event::type_sysex;          event.Type              = Event::type_sysex;
962          event.Param.Sysex.Size  = Size;          event.Param.Sysex.Size  = Size;
963          event.pEngineChannel    = NULL; // as Engine global event          event.pEngineChannel    = NULL; // as Engine global event
964            event.pMidiInputPort    = pSender;
965          if (pEventQueue->write_space() > 0) {          if (pEventQueue->write_space() > 0) {
966              if (pSysexBuffer->write_space() >= Size) {              if (pSysexBuffer->write_space() >= Size) {
967                  // copy sysex data to input buffer                  // copy sysex data to input buffer
# Line 608  namespace LinuxSampler { namespace gig { Line 993  namespace LinuxSampler { namespace gig {
993          if (pEngineChannel->GetMute()) return; // skip if sampler channel is muted          if (pEngineChannel->GetMute()) return; // skip if sampler channel is muted
994          #endif          #endif
995    
996            if (!pEngineChannel->pInstrument) return; // ignore if no instrument loaded
997    
998            //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
999            itNoteOnEvent->Param.Note.Key += pEngineChannel->GlobalTranspose;
1000    
1001          const int key = itNoteOnEvent->Param.Note.Key;          const int key = itNoteOnEvent->Param.Note.Key;
1002          midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[key];          midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[key];
1003    
# Line 665  namespace LinuxSampler { namespace gig { Line 1055  namespace LinuxSampler { namespace gig {
1055          {          {
1056              // first, get total amount of required voices (dependant on amount of layers)              // first, get total amount of required voices (dependant on amount of layers)
1057              ::gig::Region* pRegion = pEngineChannel->pInstrument->GetRegion(itNoteOnEventOnKeyList->Param.Note.Key);              ::gig::Region* pRegion = pEngineChannel->pInstrument->GetRegion(itNoteOnEventOnKeyList->Param.Note.Key);
1058              if (pRegion) {              if (pRegion && !RegionSuspended(pRegion)) {
1059                  int voicesRequired = pRegion->Layers;                  int voicesRequired = pRegion->Layers;
1060                  // now launch the required amount of voices                  // now launch the required amount of voices
1061                  for (int i = 0; i < voicesRequired; i++)                  for (int i = 0; i < voicesRequired; i++)
# Line 695  namespace LinuxSampler { namespace gig { Line 1085  namespace LinuxSampler { namespace gig {
1085          if (pEngineChannel->GetMute()) return; // skip if sampler channel is muted          if (pEngineChannel->GetMute()) return; // skip if sampler channel is muted
1086          #endif          #endif
1087    
1088            //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
1089            itNoteOffEvent->Param.Note.Key += pEngineChannel->GlobalTranspose;
1090    
1091          const int iKey = itNoteOffEvent->Param.Note.Key;          const int iKey = itNoteOffEvent->Param.Note.Key;
1092          midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[iKey];          midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[iKey];
1093          pKey->KeyPressed = false; // the MIDI key was now released          pKey->KeyPressed = false; // the MIDI key was now released
# Line 705  namespace LinuxSampler { namespace gig { Line 1098  namespace LinuxSampler { namespace gig {
1098          bool bShouldRelease = pKey->Active && ShouldReleaseVoice(pEngineChannel, itNoteOffEventOnKeyList->Param.Note.Key);          bool bShouldRelease = pKey->Active && ShouldReleaseVoice(pEngineChannel, itNoteOffEventOnKeyList->Param.Note.Key);
1099    
1100          // in case Solo Mode is enabled, kill all voices on this key and respawn a voice on the highest pressed key (if any)          // in case Solo Mode is enabled, kill all voices on this key and respawn a voice on the highest pressed key (if any)
1101          if (pEngineChannel->SoloMode) { //TODO: this feels like too much code just for handling solo mode :P          if (pEngineChannel->SoloMode && pEngineChannel->pInstrument) { //TODO: this feels like too much code just for handling solo mode :P
1102              bool bOtherKeysPressed = false;              bool bOtherKeysPressed = false;
1103              if (iKey == pEngineChannel->SoloKey) {              if (iKey == pEngineChannel->SoloKey) {
1104                  pEngineChannel->SoloKey = -1;                  pEngineChannel->SoloKey = -1;
# Line 768  namespace LinuxSampler { namespace gig { Line 1161  namespace LinuxSampler { namespace gig {
1161              itNoteOffEventOnKeyList->Type = Event::type_release; // transform event type              itNoteOffEventOnKeyList->Type = Event::type_release; // transform event type
1162    
1163              // spawn release triggered voice(s) if needed              // spawn release triggered voice(s) if needed
1164              if (pKey->ReleaseTrigger) {              if (pKey->ReleaseTrigger && pEngineChannel->pInstrument) {
1165                  // first, get total amount of required voices (dependant on amount of layers)                  // first, get total amount of required voices (dependant on amount of layers)
1166                  ::gig::Region* pRegion = pEngineChannel->pInstrument->GetRegion(itNoteOffEventOnKeyList->Param.Note.Key);                  ::gig::Region* pRegion = pEngineChannel->pInstrument->GetRegion(itNoteOffEventOnKeyList->Param.Note.Key);
1167                  if (pRegion) {                  if (pRegion) {
# Line 963  namespace LinuxSampler { namespace gig { Line 1356  namespace LinuxSampler { namespace gig {
1356                      std::cerr << "gig::Engine::LaunchVoice() Error: Unknown dimension\n" << std::flush;                      std::cerr << "gig::Engine::LaunchVoice() Error: Unknown dimension\n" << std::flush;
1357              }              }
1358          }          }
1359    
1360            // return if this is a release triggered voice and there is no
1361            // releasetrigger dimension (could happen if an instrument
1362            // change has occured between note on and off)
1363            if (ReleaseTriggerVoice && VoiceType != Voice::type_release_trigger) return Pool<Voice>::Iterator();
1364    
1365          ::gig::DimensionRegion* pDimRgn = pRegion->GetDimensionRegionByValue(DimValues);          ::gig::DimensionRegion* pDimRgn = pRegion->GetDimensionRegionByValue(DimValues);
1366    
1367          // no need to continue if sample is silent          // no need to continue if sample is silent
# Line 1192  namespace LinuxSampler { namespace gig { Line 1591  namespace LinuxSampler { namespace gig {
1591    
1592              uint keygroup = itVoice->KeyGroup;              uint keygroup = itVoice->KeyGroup;
1593    
1594                // if the sample and dimension region belong to an
1595                // instrument that is unloaded, tell the disk thread to
1596                // release them
1597                if (itVoice->Orphan) {
1598                    pDiskThread->OrderDeletionOfDimreg(itVoice->pDimRgn);
1599                }
1600    
1601              // free the voice object              // free the voice object
1602              pVoicePool->free(itVoice);              pVoicePool->free(itVoice);
1603    
# Line 1233  namespace LinuxSampler { namespace gig { Line 1639  namespace LinuxSampler { namespace gig {
1639      void Engine::ProcessControlChange(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itControlChangeEvent) {      void Engine::ProcessControlChange(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itControlChangeEvent) {
1640          dmsg(4,("Engine::ContinuousController cc=%d v=%d\n", itControlChangeEvent->Param.CC.Controller, itControlChangeEvent->Param.CC.Value));          dmsg(4,("Engine::ContinuousController cc=%d v=%d\n", itControlChangeEvent->Param.CC.Controller, itControlChangeEvent->Param.CC.Value));
1641    
1642            // handle the "control triggered" MIDI rule: a control change
1643            // event can trigger a new note on or note off event
1644            if (pEngineChannel->pInstrument) {
1645    
1646                ::gig::MidiRule* rule;
1647                for (int i = 0 ; (rule = pEngineChannel->pInstrument->GetMidiRule(i)) ; i++) {
1648    
1649                    if (::gig::MidiRuleCtrlTrigger* ctrlTrigger =
1650                        dynamic_cast< ::gig::MidiRuleCtrlTrigger*>(rule)) {
1651                        if (itControlChangeEvent->Param.CC.Controller ==
1652                            ctrlTrigger->ControllerNumber) {
1653    
1654                            uint8_t oldCCValue = pEngineChannel->ControllerTable[
1655                                itControlChangeEvent->Param.CC.Controller];
1656                            uint8_t newCCValue = itControlChangeEvent->Param.CC.Value;
1657    
1658                            for (int i = 0 ; i < ctrlTrigger->Triggers ; i++) {
1659                                ::gig::MidiRuleCtrlTrigger::trigger_t* pTrigger =
1660                                      &ctrlTrigger->pTriggers[i];
1661    
1662                                // check if the controller has passed the
1663                                // trigger point in the right direction
1664                                if ((pTrigger->Descending &&
1665                                     oldCCValue > pTrigger->TriggerPoint &&
1666                                     newCCValue <= pTrigger->TriggerPoint) ||
1667                                    (!pTrigger->Descending &&
1668                                     oldCCValue < pTrigger->TriggerPoint &&
1669                                     newCCValue >= pTrigger->TriggerPoint)) {
1670    
1671                                    RTList<Event>::Iterator itNewEvent = pGlobalEvents->allocAppend();
1672                                    if (itNewEvent) {
1673                                        *itNewEvent = *itControlChangeEvent;
1674                                        itNewEvent->Param.Note.Key = pTrigger->Key;
1675    
1676                                        if (pTrigger->NoteOff || pTrigger->Velocity == 0) {
1677                                            itNewEvent->Type = Event::type_note_off;
1678                                            itNewEvent->Param.Note.Velocity = 100;
1679    
1680                                            ProcessNoteOff(pEngineChannel, itNewEvent);
1681                                        } else {
1682                                            itNewEvent->Type = Event::type_note_on;
1683                                            //TODO: if Velocity is 255, the triggered velocity should
1684                                            // depend on how fast the controller is moving
1685                                            itNewEvent->Param.Note.Velocity =
1686                                                pTrigger->Velocity == 255 ? 100 :
1687                                                pTrigger->Velocity;
1688    
1689                                            ProcessNoteOn(pEngineChannel, itNewEvent);
1690                                        }
1691                                    }
1692                                    else dmsg(1,("Event pool emtpy!\n"));
1693                                }
1694                            }
1695                        }
1696                    }
1697                }
1698            }
1699    
1700          // update controller value in the engine channel's controller table          // update controller value in the engine channel's controller table
1701          pEngineChannel->ControllerTable[itControlChangeEvent->Param.CC.Controller] = itControlChangeEvent->Param.CC.Value;          pEngineChannel->ControllerTable[itControlChangeEvent->Param.CC.Controller] = itControlChangeEvent->Param.CC.Value;
1702    
1703            // handle hard coded MIDI controllers
1704          switch (itControlChangeEvent->Param.CC.Controller) {          switch (itControlChangeEvent->Param.CC.Controller) {
1705              case 5: { // portamento time              case 5: { // portamento time
1706                  pEngineChannel->PortamentoTime = (float) itControlChangeEvent->Param.CC.Value / 127.0f * (float) CONFIG_PORTAMENTO_TIME_MAX + (float) CONFIG_PORTAMENTO_TIME_MIN;                  pEngineChannel->PortamentoTime = (float) itControlChangeEvent->Param.CC.Value / 127.0f * (float) CONFIG_PORTAMENTO_TIME_MAX + (float) CONFIG_PORTAMENTO_TIME_MIN;
1707                  break;                  break;
1708              }              }
1709                case 6: { // data entry (currently only used for RPN controllers)
1710                    if (pEngineChannel->GetMidiRpnController() == 2) { // coarse tuning in half tones
1711                        int transpose = (int) itControlChangeEvent->Param.CC.Value - 64;
1712                        // limit to +- two octaves for now
1713                        transpose = RTMath::Min(transpose,  24);
1714                        transpose = RTMath::Max(transpose, -24);
1715                        pEngineChannel->GlobalTranspose = transpose;
1716                        // workaround, so we won't have hanging notes
1717                        ReleaseAllVoices(pEngineChannel, itControlChangeEvent);
1718                    }
1719                    // to avoid other MIDI CC #6 messages to be misenterpreted as RPN controller data
1720                    pEngineChannel->ResetMidiRpnController();
1721                    break;
1722                }
1723              case 7: { // volume              case 7: { // volume
1724                  //TODO: not sample accurate yet                  //TODO: not sample accurate yet
1725                  pEngineChannel->GlobalVolume = VolumeCurve[itControlChangeEvent->Param.CC.Value] *  CONFIG_GLOBAL_ATTENUATION;                  pEngineChannel->MidiVolume = VolumeCurve[itControlChangeEvent->Param.CC.Value];
1726                  pEngineChannel->bStatusChanged = true; // engine channel status has changed, so set notify flag                  pEngineChannel->bStatusChanged = true; // engine channel status has changed, so set notify flag
1727                  break;                  break;
1728              }              }
# Line 1251  namespace LinuxSampler { namespace gig { Line 1730  namespace LinuxSampler { namespace gig {
1730                  //TODO: not sample accurate yet                  //TODO: not sample accurate yet
1731                  pEngineChannel->GlobalPanLeft  = PanCurve[128 - itControlChangeEvent->Param.CC.Value];                  pEngineChannel->GlobalPanLeft  = PanCurve[128 - itControlChangeEvent->Param.CC.Value];
1732                  pEngineChannel->GlobalPanRight = PanCurve[itControlChangeEvent->Param.CC.Value];                  pEngineChannel->GlobalPanRight = PanCurve[itControlChangeEvent->Param.CC.Value];
1733                    pEngineChannel->iLastPanRequest = itControlChangeEvent->Param.CC.Value;
1734                  break;                  break;
1735              }              }
1736              case 64: { // sustain              case 64: { // sustain
# Line 1301  namespace LinuxSampler { namespace gig { Line 1781  namespace LinuxSampler { namespace gig {
1781                  break;                  break;
1782              }              }
1783              case 65: { // portamento on / off              case 65: { // portamento on / off
1784                  KillAllVoices(pEngineChannel, itControlChangeEvent);                  const bool bPortamento = itControlChangeEvent->Param.CC.Value >= 64;
1785                  pEngineChannel->PortamentoMode = itControlChangeEvent->Param.CC.Value >= 64;                  if (bPortamento != pEngineChannel->PortamentoMode)
1786                        KillAllVoices(pEngineChannel, itControlChangeEvent);
1787                    pEngineChannel->PortamentoMode = bPortamento;
1788                  break;                  break;
1789              }              }
1790              case 66: { // sostenuto              case 66: { // sostenuto
# Line 1345  namespace LinuxSampler { namespace gig { Line 1827  namespace LinuxSampler { namespace gig {
1827                  }                  }
1828                  break;                  break;
1829              }              }
1830                case 100: { // RPN controller LSB
1831                    pEngineChannel->SetMidiRpnControllerLsb(itControlChangeEvent->Param.CC.Value);
1832                    break;
1833                }
1834                case 101: { // RPN controller MSB
1835                    pEngineChannel->SetMidiRpnControllerMsb(itControlChangeEvent->Param.CC.Value);
1836                    break;
1837                }
1838    
1839    
1840              // Channel Mode Messages              // Channel Mode Messages
# Line 1364  namespace LinuxSampler { namespace gig { Line 1854  namespace LinuxSampler { namespace gig {
1854                  break;                  break;
1855              }              }
1856              case 126: { // mono mode on              case 126: { // mono mode on
1857                  KillAllVoices(pEngineChannel, itControlChangeEvent);                  if (!pEngineChannel->SoloMode)
1858                        KillAllVoices(pEngineChannel, itControlChangeEvent);
1859                  pEngineChannel->SoloMode = true;                  pEngineChannel->SoloMode = true;
1860                  break;                  break;
1861              }              }
1862              case 127: { // poly mode on              case 127: { // poly mode on
1863                  KillAllVoices(pEngineChannel, itControlChangeEvent);                  if (pEngineChannel->SoloMode)
1864                        KillAllVoices(pEngineChannel, itControlChangeEvent);
1865                  pEngineChannel->SoloMode = false;                  pEngineChannel->SoloMode = false;
1866                  break;                  break;
1867              }              }
1868          }          }
1869    
1870            // handle FX send controllers
1871            if (!pEngineChannel->fxSends.empty()) {
1872                for (int iFxSend = 0; iFxSend < pEngineChannel->GetFxSendCount(); iFxSend++) {
1873                    FxSend* pFxSend = pEngineChannel->GetFxSend(iFxSend);
1874                    if (pFxSend->MidiController() == itControlChangeEvent->Param.CC.Controller) {
1875                        pFxSend->SetLevel(itControlChangeEvent->Param.CC.Value);
1876                        pFxSend->SetInfoChanged(true);
1877                    }
1878                }
1879            }
1880      }      }
1881    
1882      /**      /**
# Line 1382  namespace LinuxSampler { namespace gig { Line 1885  namespace LinuxSampler { namespace gig {
1885       *  @param itSysexEvent - sysex data size and time stamp of the sysex event       *  @param itSysexEvent - sysex data size and time stamp of the sysex event
1886       */       */
1887      void Engine::ProcessSysex(Pool<Event>::Iterator& itSysexEvent) {      void Engine::ProcessSysex(Pool<Event>::Iterator& itSysexEvent) {
1888          RingBuffer<uint8_t>::NonVolatileReader reader = pSysexBuffer->get_non_volatile_reader();          RingBuffer<uint8_t,false>::NonVolatileReader reader = pSysexBuffer->get_non_volatile_reader();
1889    
1890          uint8_t exclusive_status, id;          uint8_t exclusive_status, id;
1891          if (!reader.pop(&exclusive_status)) goto free_sysex_data;          if (!reader.pop(&exclusive_status)) goto free_sysex_data;
# Line 1390  namespace LinuxSampler { namespace gig { Line 1893  namespace LinuxSampler { namespace gig {
1893          if (exclusive_status != 0xF0)       goto free_sysex_data;          if (exclusive_status != 0xF0)       goto free_sysex_data;
1894    
1895          switch (id) {          switch (id) {
1896                case 0x7f: { // (Realtime) Universal Sysex (GM Standard)
1897                    uint8_t sysex_channel, sub_id1, sub_id2, val_msb, val_lsb;;
1898                    if (!reader.pop(&sysex_channel)) goto free_sysex_data;
1899                    if (!reader.pop(&sub_id1)) goto free_sysex_data;
1900                    if (!reader.pop(&sub_id2)) goto free_sysex_data;
1901                    if (!reader.pop(&val_lsb)) goto free_sysex_data;
1902                    if (!reader.pop(&val_msb)) goto free_sysex_data;
1903                    //TODO: for now we simply ignore the sysex channel, seldom used anyway
1904                    switch (sub_id1) {
1905                        case 0x04: // Device Control
1906                            switch (sub_id2) {
1907                                case 0x01: // Master Volume
1908                                    GLOBAL_VOLUME =
1909                                        double((uint(val_msb)<<7) | uint(val_lsb)) / 16383.0;
1910                                    break;
1911                            }
1912                            break;
1913                    }
1914                    break;
1915                }
1916              case 0x41: { // Roland              case 0x41: { // Roland
1917                  dmsg(3,("Roland Sysex\n"));                  dmsg(3,("Roland Sysex\n"));
1918                  uint8_t device_id, model_id, cmd_id;                  uint8_t device_id, model_id, cmd_id;
# Line 1401  namespace LinuxSampler { namespace gig { Line 1924  namespace LinuxSampler { namespace gig {
1924    
1925                  // command address                  // command address
1926                  uint8_t addr[3]; // 2 byte addr MSB, followed by 1 byte addr LSB)                  uint8_t addr[3]; // 2 byte addr MSB, followed by 1 byte addr LSB)
1927                  const RingBuffer<uint8_t>::NonVolatileReader checksum_reader = reader; // so we can calculate the check sum later                  const RingBuffer<uint8_t,false>::NonVolatileReader checksum_reader = reader; // so we can calculate the check sum later
1928                  if (reader.read(&addr[0], 3) != 3) goto free_sysex_data;                  if (reader.read(&addr[0], 3) != 3) goto free_sysex_data;
1929                  if (addr[0] == 0x40 && addr[1] == 0x00) { // System Parameters                  if (addr[0] == 0x40 && addr[1] == 0x00) { // System Parameters
1930                      dmsg(3,("\tSystem Parameter\n"));                      dmsg(3,("\tSystem Parameter\n"));
# Line 1426  namespace LinuxSampler { namespace gig { Line 1949  namespace LinuxSampler { namespace gig {
1949                              dmsg(3,("\t\t\tNew scale applied.\n"));                              dmsg(3,("\t\t\tNew scale applied.\n"));
1950                              break;                              break;
1951                          }                          }
1952                            case 0x15: { // chromatic / drumkit mode
1953                                dmsg(3,("\t\tMIDI Instrument Map Switch\n"));
1954                                uint8_t part = addr[1] & 0x0f;
1955                                uint8_t map;
1956                                if (!reader.pop(&map)) goto free_sysex_data;
1957                                for (int i = 0; i < engineChannels.size(); ++i) {
1958                                    EngineChannel* pEngineChannel = engineChannels[i];
1959                                    if (
1960                                        (pEngineChannel->midiChannel == part ||
1961                                         pEngineChannel->midiChannel == midi_chan_all) &&
1962                                        pEngineChannel->GetMidiInputPort() == itSysexEvent->pMidiInputPort
1963                                    ) {
1964                                        try {
1965                                            pEngineChannel->SetMidiInstrumentMap(map);
1966                                        } catch (Exception e) {
1967                                            dmsg(2,("\t\t\tCould not apply MIDI instrument map %d to part %d: %s\n", map, part, e.Message().c_str()));
1968                                            goto free_sysex_data;
1969                                        } catch (...) {
1970                                            dmsg(2,("\t\t\tCould not apply MIDI instrument map %d to part %d (unknown exception)\n", map, part));
1971                                            goto free_sysex_data;
1972                                        }
1973                                    }
1974                                }
1975                                dmsg(3,("\t\t\tApplied MIDI instrument map %d to part %d.\n", map, part));
1976                                break;
1977                            }
1978                      }                      }
1979                  }                  }
1980                  else if (addr[0] == 0x40 && (addr[1] & 0xf0) == 0x20) { // Part Parameters (2)                  else if (addr[0] == 0x40 && (addr[1] & 0xf0) == 0x20) { // Part Parameters (2)
# Line 1448  namespace LinuxSampler { namespace gig { Line 1997  namespace LinuxSampler { namespace gig {
1997       *                     question       *                     question
1998       * @param DataSize   - size of the GS message data (in bytes)       * @param DataSize   - size of the GS message data (in bytes)
1999       */       */
2000      uint8_t Engine::GSCheckSum(const RingBuffer<uint8_t>::NonVolatileReader AddrReader, uint DataSize) {      uint8_t Engine::GSCheckSum(const RingBuffer<uint8_t,false>::NonVolatileReader AddrReader, uint DataSize) {
2001          RingBuffer<uint8_t>::NonVolatileReader reader = AddrReader;          RingBuffer<uint8_t,false>::NonVolatileReader reader = AddrReader;
2002          uint bytes = 3 /*addr*/ + DataSize;          uint bytes = 3 /*addr*/ + DataSize;
2003          uint8_t addr_and_data[bytes];          uint8_t addr_and_data[bytes];
2004          reader.read(&addr_and_data[0], bytes);          reader.read(&addr_and_data[0], bytes);
# Line 1564  namespace LinuxSampler { namespace gig { Line 2113  namespace LinuxSampler { namespace gig {
2113      }      }
2114    
2115      String Engine::Description() {      String Engine::Description() {
2116          return "Gigasampler Engine";          return "Gigasampler Format Engine";
2117      }      }
2118    
2119      String Engine::Version() {      String Engine::Version() {
2120          String s = "$Revision: 1.65 $";          String s = "$Revision: 1.95 $";
2121          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
2122      }      }
2123    
2124        InstrumentManager* Engine::GetInstrumentManager() {
2125            return &instruments;
2126        }
2127    
2128      // static constant initializers      // static constant initializers
2129      const float* Engine::VolumeCurve(InitVolumeCurve());      const Engine::FloatTable Engine::VolumeCurve(InitVolumeCurve());
2130      const float* Engine::PanCurve(InitPanCurve());      const Engine::FloatTable Engine::PanCurve(InitPanCurve());
2131      const float* Engine::CrossfadeCurve(InitCrossfadeCurve());      const Engine::FloatTable Engine::CrossfadeCurve(InitCrossfadeCurve());
2132    
2133      float* Engine::InitVolumeCurve() {      float* Engine::InitVolumeCurve() {
2134          // line-segment approximation          // line-segment approximation

Legend:
Removed from v.924  
changed lines
  Added in v.1751

  ViewVC Help
Powered by ViewVC