/[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 781 by schoenebeck, Mon Sep 26 10:17:00 2005 UTC revision 1644 by persson, Sat Jan 19 16:55:03 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 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 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            pDimRegionsInUse   = new ::gig::DimensionRegion*[CONFIG_MAX_VOICES + 1];
112          pVoiceStealingQueue = new RTList<Event>(pEventPool);          pVoiceStealingQueue = new RTList<Event>(pEventPool);
113          pGlobalEvents      = new RTList<Event>(pEventPool);          pGlobalEvents      = new RTList<Event>(pEventPool);
114            InstrumentChangeQueue      = new RingBuffer<instrument_change_command_t,false>(1, 0);
115            InstrumentChangeReplyQueue = new RingBuffer<instrument_change_reply_t,false>(1, 0);
116    
117          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()) {
118              iterVoice->SetEngine(this);              iterVoice->SetEngine(this);
119          }          }
# Line 110  namespace LinuxSampler { namespace gig { Line 121  namespace LinuxSampler { namespace gig {
121    
122          ResetInternal();          ResetInternal();
123          ResetScaleTuning();          ResetScaleTuning();
124            ResetSuspendedRegions();
125      }      }
126    
127      /**      /**
128       * Destructor       * Destructor
129       */       */
130      Engine::~Engine() {      Engine::~Engine() {
131            MidiInputPort::RemoveSysexListener(this);
132          if (pDiskThread) {          if (pDiskThread) {
133              dmsg(1,("Stopping disk thread..."));              dmsg(1,("Stopping disk thread..."));
134              pDiskThread->StopThread();              pDiskThread->StopThread();
# Line 131  namespace LinuxSampler { namespace gig { Line 144  namespace LinuxSampler { namespace gig {
144          if (pEventGenerator) delete pEventGenerator;          if (pEventGenerator) delete pEventGenerator;
145          if (pVoiceStealingQueue) delete pVoiceStealingQueue;          if (pVoiceStealingQueue) delete pVoiceStealingQueue;
146          if (pSysexBuffer) delete pSysexBuffer;          if (pSysexBuffer) delete pSysexBuffer;
147          EngineFactory::Destroy(this);          if (pGlobalEvents) delete pGlobalEvents;
148            if (InstrumentChangeQueue) delete InstrumentChangeQueue;
149            if (InstrumentChangeReplyQueue) delete InstrumentChangeReplyQueue;
150            if (pDimRegionsInUse) delete[] pDimRegionsInUse;
151            ResetSuspendedRegions();
152            Unregister();
153      }      }
154    
155      void Engine::Enable() {      void Engine::Enable() {
# Line 140  namespace LinuxSampler { namespace gig { Line 158  namespace LinuxSampler { namespace gig {
158          dmsg(3,("gig::Engine: enabled (val=%d)\n", EngineDisabled.GetUnsafe()));          dmsg(3,("gig::Engine: enabled (val=%d)\n", EngineDisabled.GetUnsafe()));
159      }      }
160    
161        /**
162         * Temporarily stop the engine to not do anything. The engine will just be
163         * frozen during that time, that means after enabling it again it will
164         * continue where it was, with all its voices and playback state it had at
165         * the point of disabling. Notice that the engine's (audio) thread will
166         * continue to run, it just remains in an inactive loop during that time.
167         *
168         * If you need to be sure that all voices and disk streams are killed as
169         * well, use @c SuspendAll() instead.
170         *
171         * @see Enable(), SuspendAll()
172         */
173      void Engine::Disable() {      void Engine::Disable() {
174          dmsg(3,("gig::Engine: disabling\n"));          dmsg(3,("gig::Engine: disabling\n"));
175          bool* pWasDisabled = EngineDisabled.PushAndUnlock(true, 2); // wait max. 2s          bool* pWasDisabled = EngineDisabled.PushAndUnlock(true, 2); // wait max. 2s
# Line 153  namespace LinuxSampler { namespace gig { Line 183  namespace LinuxSampler { namespace gig {
183      }      }
184    
185      /**      /**
186         * Similar to @c Disable() but this method additionally kills all voices
187         * and disk streams and blocks until all voices and disk streams are actually
188         * killed / deleted.
189         *
190         * @e Note: only the original calling thread is able to re-enable the
191         * engine afterwards by calling @c ResumeAll() later on!
192         */
193        void Engine::SuspendAll() {
194            dmsg(2,("gig::Engine: Suspending all ...\n"));
195            // stop the engine, so we can safely modify the engine's
196            // data structures from this foreign thread
197            DisableAndLock();
198            // we could also use the respective class member variable here,
199            // but this is probably safer and cleaner
200            int iPendingStreamDeletions = 0;
201            // kill all voices on all engine channels the *die hard* way
202            for (int iChannel = 0; iChannel < engineChannels.size(); iChannel++) {
203                EngineChannel* pEngineChannel = engineChannels[iChannel];
204                RTList<uint>::Iterator iuiKey = pEngineChannel->pActiveKeys->first();
205                RTList<uint>::Iterator end    = pEngineChannel->pActiveKeys->end();
206                for (; iuiKey != end; ++iuiKey) { // iterate through all active keys
207                    midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[*iuiKey];
208                    RTList<Voice>::Iterator itVoice = pKey->pActiveVoices->first();
209                    RTList<Voice>::Iterator itVoicesEnd = pKey->pActiveVoices->end();
210                    for (; itVoice != itVoicesEnd; ++itVoice) { // iterate through all voices on this key
211                        // request a notification from disk thread side for stream deletion
212                        const Stream::Handle hStream = itVoice->KillImmediately(true);
213                        if (hStream != Stream::INVALID_HANDLE) { // voice actually used a stream
214                            iPendingStreamDeletions++;
215                        }
216                    }
217                }
218            }
219            // wait until all streams were actually deleted by the disk thread
220            while (iPendingStreamDeletions) {
221                while (
222                    iPendingStreamDeletions &&
223                    pDiskThread->AskForDeletedStream() != Stream::INVALID_HANDLE
224                ) iPendingStreamDeletions--;
225                if (!iPendingStreamDeletions) break;
226                usleep(10000); // sleep for 10ms
227            }
228            dmsg(2,("gig::Engine: Everything suspended.\n"));
229        }
230    
231        /**
232         * At the moment same as calling @c Enable() directly, but this might
233         * change in future, so better call this method as counterpart to
234         * @c SuspendAll() instead of @c Enable() !
235         */
236        void Engine::ResumeAll() {
237            Enable();
238        }
239    
240        /**
241         * Order the engine to stop rendering audio for the given region.
242         * Additionally this method will block until all voices and their disk
243         * streams associated with that region are actually killed / deleted, so
244         * one can i.e. safely modify the region with an instrument editor after
245         * returning from this method.
246         *
247         * @param pRegion - region the engine shall stop using
248         */
249        void Engine::Suspend(::gig::Region* pRegion) {
250            dmsg(2,("gig::Engine: Suspending Region %x ...\n",pRegion));
251            SuspendedRegionsMutex.Lock();
252            SuspensionChangeOngoing.Set(true);
253            pPendingRegionSuspension = pRegion;
254            SuspensionChangeOngoing.WaitAndUnlockIf(true);
255            SuspendedRegionsMutex.Unlock();
256            dmsg(2,("gig::Engine: Region %x suspended.",pRegion));
257        }
258    
259        /**
260         * Orders the engine to resume playing back the given region, previously
261         * suspended with @c Suspend() .
262         *
263         * @param pRegion - region the engine shall be allowed to use again
264         */
265        void Engine::Resume(::gig::Region* pRegion) {
266            dmsg(2,("gig::Engine: Resuming Region %x ...\n",pRegion));
267            SuspendedRegionsMutex.Lock();
268            SuspensionChangeOngoing.Set(true);
269            pPendingRegionResumption = pRegion;
270            SuspensionChangeOngoing.WaitAndUnlockIf(true);
271            SuspendedRegionsMutex.Unlock();
272            dmsg(2,("gig::Engine: Region %x resumed.\n",pRegion));
273        }
274    
275        /**
276       *  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
277       *  control and status variables.       *  control and status variables.
278       */       */
# Line 165  namespace LinuxSampler { namespace gig { Line 285  namespace LinuxSampler { namespace gig {
285    
286      /**      /**
287       *  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
288       *  control and status variables. This method is not thread safe!       *  control and status variables. This method is protected by a mutex.
289       */       */
290      void Engine::ResetInternal() {      void Engine::ResetInternal() {
291            ResetInternalMutex.Lock();
292    
293            // make sure that the engine does not get any sysex messages
294            // while it's reseting
295            bool sysexDisabled = MidiInputPort::RemoveSysexListener(this);
296          ActiveVoiceCount    = 0;          ActiveVoiceCount    = 0;
297          ActiveVoiceCountMax = 0;          ActiveVoiceCountMax = 0;
298    
# Line 191  namespace LinuxSampler { namespace gig { Line 316  namespace LinuxSampler { namespace gig {
316          // delete all input events          // delete all input events
317          pEventQueue->init();          pEventQueue->init();
318          pSysexBuffer->init();          pSysexBuffer->init();
319            if (sysexDisabled) MidiInputPort::AddSysexListener(this);
320            ResetInternalMutex.Unlock();
321      }      }
322    
323      /**      /**
# Line 200  namespace LinuxSampler { namespace gig { Line 327  namespace LinuxSampler { namespace gig {
327          memset(&ScaleTuning[0], 0x00, 12);          memset(&ScaleTuning[0], 0x00, 12);
328      }      }
329    
330        void Engine::ResetSuspendedRegions() {
331            SuspendedRegions.clear();
332            iPendingStreamDeletions = 0;
333            pPendingRegionSuspension = pPendingRegionResumption = NULL;
334            SuspensionChangeOngoing.Set(false);
335        }
336    
337      /**      /**
338       * Connect this engine instance with the given audio output device.       * Connect this engine instance with the given audio output device.
339       * This method will be called when an Engine instance is created.       * This method will be called when an Engine instance is created.
# Line 220  namespace LinuxSampler { namespace gig { Line 354  namespace LinuxSampler { namespace gig {
354          }          }
355          catch (AudioOutputException e) {          catch (AudioOutputException e) {
356              String msg = "Audio output device unable to provide 2 audio channels, cause: " + e.Message();              String msg = "Audio output device unable to provide 2 audio channels, cause: " + e.Message();
357              throw LinuxSamplerException(msg);              throw Exception(msg);
358          }          }
359    
360          this->MaxSamplesPerCycle = pAudioOutputDevice->MaxSamplesPerCycle();          this->MaxSamplesPerCycle = pAudioOutputDevice->MaxSamplesPerCycle();
# Line 249  namespace LinuxSampler { namespace gig { Line 383  namespace LinuxSampler { namespace gig {
383              delete this->pDiskThread;              delete this->pDiskThread;
384              dmsg(1,("OK\n"));              dmsg(1,("OK\n"));
385          }          }
386          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
387                                               &instruments);
388          if (!pDiskThread) {          if (!pDiskThread) {
389              dmsg(0,("gig::Engine  new diskthread = NULL\n"));              dmsg(0,("gig::Engine  new diskthread = NULL\n"));
390              exit(EXIT_FAILURE);              exit(EXIT_FAILURE);
# Line 278  namespace LinuxSampler { namespace gig { Line 413  namespace LinuxSampler { namespace gig {
413      }      }
414    
415      /**      /**
416         * Called by the engine's (audio) thread once per cycle to process requests
417         * from the outer world to suspend or resume a given @c gig::Region .
418         */
419        void Engine::ProcessSuspensionsChanges() {
420            // process request for suspending one region
421            if (pPendingRegionSuspension) {
422                // kill all voices on all engine channels that use this region
423                for (int iChannel = 0; iChannel < engineChannels.size(); iChannel++) {
424                    EngineChannel* pEngineChannel = engineChannels[iChannel];
425                    RTList<uint>::Iterator iuiKey = pEngineChannel->pActiveKeys->first();
426                    RTList<uint>::Iterator end    = pEngineChannel->pActiveKeys->end();
427                    for (; iuiKey != end; ++iuiKey) { // iterate through all active keys
428                        midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[*iuiKey];
429                        RTList<Voice>::Iterator itVoice = pKey->pActiveVoices->first();
430                        // if current key is not associated with this region, skip this key
431                        if (itVoice->pDimRgn->GetParent() != pPendingRegionSuspension) continue;
432                        RTList<Voice>::Iterator itVoicesEnd = pKey->pActiveVoices->end();
433                        for (; itVoice != itVoicesEnd; ++itVoice) { // iterate through all voices on this key
434                            // request a notification from disk thread side for stream deletion
435                            const Stream::Handle hStream = itVoice->KillImmediately(true);
436                            if (hStream != Stream::INVALID_HANDLE) { // voice actually used a stream
437                                iPendingStreamDeletions++;
438                            }
439                        }
440                    }
441                }
442                // make sure the region is not yet on the list
443                bool bAlreadySuspended = false;
444                RTList< ::gig::Region*>::Iterator iter = SuspendedRegions.first();
445                RTList< ::gig::Region*>::Iterator end  = SuspendedRegions.end();
446                for (; iter != end; ++iter) { // iterate through all suspended regions
447                    if (*iter == pPendingRegionSuspension) { // found
448                        bAlreadySuspended = true;
449                        dmsg(1,("gig::Engine: attempt to suspend an already suspended region !!!\n"));
450                        break;
451                    }
452                }
453                if (!bAlreadySuspended) {
454                    // put the region on the list of suspended regions
455                    RTList< ::gig::Region*>::Iterator iter = SuspendedRegions.allocAppend();
456                    if (iter) {
457                        *iter = pPendingRegionSuspension;
458                    } else std::cerr << "gig::Engine: Could not suspend Region, list is full. This is a bug!!!\n" << std::flush;
459                }
460                // free request slot for next caller (and to make sure that
461                // we're not going to process the same request in the next cycle)
462                pPendingRegionSuspension = NULL;
463                // if no disk stream deletions are pending, awaker other side, as
464                // we're done in this case
465                if (!iPendingStreamDeletions) SuspensionChangeOngoing.Set(false);
466            }
467    
468            // process request for resuming one region
469            if (pPendingRegionResumption) {
470                // remove region from the list of suspended regions
471                RTList< ::gig::Region*>::Iterator iter = SuspendedRegions.first();
472                RTList< ::gig::Region*>::Iterator end  = SuspendedRegions.end();
473                for (; iter != end; ++iter) { // iterate through all suspended regions
474                    if (*iter == pPendingRegionResumption) { // found
475                        SuspendedRegions.free(iter);
476                        break; // done
477                    }
478                }
479                // free request slot for next caller
480                pPendingRegionResumption = NULL;
481                // awake other side as we're done
482                SuspensionChangeOngoing.Set(false);
483            }
484        }
485    
486        /**
487         * Called by the engine's (audio) thread once per cycle to check if
488         * streams of voices that were killed due to suspension request have
489         * finally really been deleted by the disk thread.
490         */
491        void Engine::ProcessPendingStreamDeletions() {
492            if (!iPendingStreamDeletions) return;
493            //TODO: or shall we better store a list with stream handles instead of a scalar amount of streams to be deleted? might be safer
494            while (
495                iPendingStreamDeletions &&
496                pDiskThread->AskForDeletedStream() != Stream::INVALID_HANDLE
497            ) iPendingStreamDeletions--;
498            // just for safety ...
499            while (pDiskThread->AskForDeletedStream() != Stream::INVALID_HANDLE);
500            // now that all disk streams are deleted, awake other side as
501            // we're finally done with suspending the requested region
502            if (!iPendingStreamDeletions) SuspensionChangeOngoing.Set(false);
503        }
504    
505        /**
506         * Returns @c true if the given region is currently set to be suspended
507         * from being used, @c false otherwise.
508         */
509        bool Engine::RegionSuspended(::gig::Region* pRegion) {
510            if (SuspendedRegions.isEmpty()) return false;
511            //TODO: or shall we use a sorted container instead of the RTList? might be faster ... or trivial ;-)
512            RTList< ::gig::Region*>::Iterator iter = SuspendedRegions.first();
513            RTList< ::gig::Region*>::Iterator end  = SuspendedRegions.end();
514            for (; iter != end; ++iter)  // iterate through all suspended regions
515                if (*iter == pRegion) return true;
516            return false;
517        }
518    
519        /**
520       * Clear all engine global event lists.       * Clear all engine global event lists.
521       */       */
522      void Engine::ClearEventLists() {      void Engine::ClearEventLists() {
# Line 298  namespace LinuxSampler { namespace gig { Line 537  namespace LinuxSampler { namespace gig {
537       *                  current audio cycle       *                  current audio cycle
538       */       */
539      void Engine::ImportEvents(uint Samples) {      void Engine::ImportEvents(uint Samples) {
540          RingBuffer<Event>::NonVolatileReader eventQueueReader = pEventQueue->get_non_volatile_reader();          RingBuffer<Event,false>::NonVolatileReader eventQueueReader = pEventQueue->get_non_volatile_reader();
541          Event* pEvent;          Event* pEvent;
542          while (true) {          while (true) {
543              // get next event from input event queue              // get next event from input event queue
# Line 321  namespace LinuxSampler { namespace gig { Line 560  namespace LinuxSampler { namespace gig {
560      }      }
561    
562      /**      /**
563       *  Let this engine proceed to render the given amount of sample points. The       * Let this engine proceed to render the given amount of sample points.
564       *  calculated audio data of all voices of this engine will be placed into       * The engine will iterate through all engine channels and render audio
565       *  the engine's audio sum buffer which has to be copied and eventually be       * for each engine channel independently. The calculated audio data of
566       *  converted to the appropriate value range by the audio output class (e.g.       * all voices of each engine channel will be placed into the audio sum
567       *  AlsaIO or JackIO) right after.       * buffers of the respective audio output device, connected to the
568         * respective engine channel.
569       *       *
570       *  @param Samples - number of sample points to be rendered       *  @param Samples - number of sample points to be rendered
571       *  @returns       0 on success       *  @returns       0 on success
572       */       */
573      int Engine::RenderAudio(uint Samples) {      int Engine::RenderAudio(uint Samples) {
574          dmsg(5,("RenderAudio(Samples=%d)\n", Samples));          dmsg(8,("RenderAudio(Samples=%d)\n", Samples));
575    
576          // return if engine disabled          // return if engine disabled
577          if (EngineDisabled.Pop()) {          if (EngineDisabled.Pop()) {
# Line 339  namespace LinuxSampler { namespace gig { Line 579  namespace LinuxSampler { namespace gig {
579              return 0;              return 0;
580          }          }
581    
582            // process requests for suspending / resuming regions (i.e. to avoid
583            // crashes while these regions are modified by an instrument editor)
584            ProcessSuspensionsChanges();
585    
586          // 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)
587          pEventGenerator->UpdateFragmentTime(Samples);          pEventGenerator->UpdateFragmentTime(Samples);
588    
# Line 368  namespace LinuxSampler { namespace gig { Line 612  namespace LinuxSampler { namespace gig {
612          // reset internal voice counter (just for statistic of active voices)          // reset internal voice counter (just for statistic of active voices)
613          ActiveVoiceCountTemp = 0;          ActiveVoiceCountTemp = 0;
614    
615            // handle instrument change commands
616            instrument_change_command_t command;
617            if (InstrumentChangeQueue->pop(&command) > 0) {
618                EngineChannel* pEngineChannel = command.pEngineChannel;
619                pEngineChannel->pInstrument = command.pInstrument;
620    
621                //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
622                ResetSuspendedRegions();
623    
624                // iterate through all active voices and mark their
625                // dimension regions as "in use". The instrument resource
626                // manager may delete all of the instrument except the
627                // dimension regions and samples that are in use.
628                int i = 0;
629                RTList<uint>::Iterator iuiKey = pEngineChannel->pActiveKeys->first();
630                RTList<uint>::Iterator end    = pEngineChannel->pActiveKeys->end();
631                while (iuiKey != end) { // iterate through all active keys
632                    midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[*iuiKey];
633                    ++iuiKey;
634    
635                    RTList<Voice>::Iterator itVoice     = pKey->pActiveVoices->first();
636                    RTList<Voice>::Iterator itVoicesEnd = pKey->pActiveVoices->end();
637                    for (; itVoice != itVoicesEnd; ++itVoice) { // iterate through all voices on this key
638                        if (!itVoice->Orphan) {
639                            itVoice->Orphan = true;
640                            pDimRegionsInUse[i++] = itVoice->pDimRgn;
641                        }
642                    }
643                }
644                pDimRegionsInUse[i] = 0; // end of list
645    
646                // send a reply to the calling thread, which is waiting
647                instrument_change_reply_t reply;
648                InstrumentChangeReplyQueue->push(&reply);
649            }
650    
651          // handle events on all engine channels          // handle events on all engine channels
652          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  
653              ProcessEvents(engineChannels[i], Samples);              ProcessEvents(engineChannels[i], Samples);
654          }          }
655    
656          // render all 'normal', active voices on all engine channels          // render all 'normal', active voices 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              RenderActiveVoices(engineChannels[i], Samples);              RenderActiveVoices(engineChannels[i], Samples);
659          }          }
660    
661          // 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
662          RenderStolenVoices(Samples);          RenderStolenVoices(Samples);
663    
664            // handle audio routing for engine channels with FX sends
665            for (int i = 0; i < engineChannels.size(); i++) {
666                if (engineChannels[i]->fxSends.empty()) continue; // ignore if no FX sends
667                RouteAudio(engineChannels[i], Samples);
668            }
669    
670          // handle cleanup on all engine channels for the next audio fragment          // handle cleanup on all engine channels for the next audio fragment
671          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  
672              PostProcess(engineChannels[i]);              PostProcess(engineChannels[i]);
673          }          }
674    
# Line 400  namespace LinuxSampler { namespace gig { Line 683  namespace LinuxSampler { namespace gig {
683          ActiveVoiceCount = ActiveVoiceCountTemp;          ActiveVoiceCount = ActiveVoiceCountTemp;
684          if (ActiveVoiceCount > ActiveVoiceCountMax) ActiveVoiceCountMax = ActiveVoiceCount;          if (ActiveVoiceCount > ActiveVoiceCountMax) ActiveVoiceCountMax = ActiveVoiceCount;
685    
686            // in case regions were previously suspended and we killed voices
687            // with disk streams due to that, check if those streams have finally
688            // been deleted by the disk thread
689            if (iPendingStreamDeletions) ProcessPendingStreamDeletions();
690    
691          FrameTime += Samples;          FrameTime += Samples;
692    
693          return 0;          return 0;
# Line 467  namespace LinuxSampler { namespace gig { Line 755  namespace LinuxSampler { namespace gig {
755          if (pEngineChannel->GetMute()) return; // skip if sampler channel is muted          if (pEngineChannel->GetMute()) return; // skip if sampler channel is muted
756          #endif          #endif
757    
758            uint voiceCount = 0;
759            uint streamCount = 0;
760          RTList<uint>::Iterator iuiKey = pEngineChannel->pActiveKeys->first();          RTList<uint>::Iterator iuiKey = pEngineChannel->pActiveKeys->first();
761          RTList<uint>::Iterator end    = pEngineChannel->pActiveKeys->end();          RTList<uint>::Iterator end    = pEngineChannel->pActiveKeys->end();
762          while (iuiKey != end) { // iterate through all active keys          while (iuiKey != end) { // iterate through all active keys
# Line 478  namespace LinuxSampler { namespace gig { Line 768  namespace LinuxSampler { namespace gig {
768              for (; itVoice != itVoicesEnd; ++itVoice) { // iterate through all voices on this key              for (; itVoice != itVoicesEnd; ++itVoice) { // iterate through all voices on this key
769                  // now render current voice                  // now render current voice
770                  itVoice->Render(Samples);                  itVoice->Render(Samples);
771                  if (itVoice->IsActive()) ActiveVoiceCountTemp++; // still active                  if (itVoice->IsActive()) { // still active
772                  else { // voice reached end, is now inactive                      ActiveVoiceCountTemp++;
773                        voiceCount++;
774    
775                        if (itVoice->PlaybackState == Voice::playback_state_disk) {
776                            if ((itVoice->DiskStreamRef).State == Stream::state_active) streamCount++;
777                        }
778                    }  else { // voice reached end, is now inactive
779                      FreeVoice(pEngineChannel, itVoice); // remove voice from the list of active voices                      FreeVoice(pEngineChannel, itVoice); // remove voice from the list of active voices
780                  }                  }
781              }              }
782          }          }
783    
784            pEngineChannel->SetVoiceCount(voiceCount);
785            pEngineChannel->SetDiskStreamCount(streamCount);
786      }      }
787    
788      /**      /**
# Line 504  namespace LinuxSampler { namespace gig { Line 803  namespace LinuxSampler { namespace gig {
803          RTList<Event>::Iterator end               = pVoiceStealingQueue->end();          RTList<Event>::Iterator end               = pVoiceStealingQueue->end();
804          for (; itVoiceStealEvent != end; ++itVoiceStealEvent) {          for (; itVoiceStealEvent != end; ++itVoiceStealEvent) {
805              EngineChannel* pEngineChannel = (EngineChannel*) itVoiceStealEvent->pEngineChannel;              EngineChannel* pEngineChannel = (EngineChannel*) itVoiceStealEvent->pEngineChannel;
806                if (!pEngineChannel->pInstrument) continue; // ignore if no instrument loaded
807              Pool<Voice>::Iterator itNewVoice =              Pool<Voice>::Iterator itNewVoice =
808                  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);
809              if (itNewVoice) {              if (itNewVoice) {
810                  itNewVoice->Render(Samples);                  itNewVoice->Render(Samples);
811                  if (itNewVoice->IsActive()) ActiveVoiceCountTemp++; // still active                  if (itNewVoice->IsActive()) { // still active
812                  else { // voice reached end, is now inactive                      ActiveVoiceCountTemp++;
813                        pEngineChannel->SetVoiceCount(pEngineChannel->GetVoiceCount() + 1);
814    
815                        if (itNewVoice->PlaybackState == Voice::playback_state_disk) {
816                            if (itNewVoice->DiskStreamRef.State == Stream::state_active) {
817                                pEngineChannel->SetDiskStreamCount(pEngineChannel->GetDiskStreamCount() + 1);
818                            }
819                        }
820                    } else { // voice reached end, is now inactive
821                      FreeVoice(pEngineChannel, itNewVoice); // remove voice from the list of active voices                      FreeVoice(pEngineChannel, itNewVoice); // remove voice from the list of active voices
822                  }                  }
823              }              }
# Line 523  namespace LinuxSampler { namespace gig { Line 831  namespace LinuxSampler { namespace gig {
831      }      }
832    
833      /**      /**
834         * Will be called in case the respective engine channel sports FX send
835         * channels. In this particular case, engine channel local buffers are
836         * used to render and mix all voices to. This method is responsible for
837         * copying the audio data from those local buffers to the master audio
838         * output channels as well as to the FX send audio output channels with
839         * their respective FX send levels.
840         *
841         * @param pEngineChannel - engine channel from which audio should be
842         *                         routed
843         * @param Samples        - amount of sample points to be routed in
844         *                         this audio fragment cycle
845         */
846        void Engine::RouteAudio(EngineChannel* pEngineChannel, uint Samples) {
847            // route master signal
848            {
849                AudioChannel* pDstL = pAudioOutputDevice->Channel(pEngineChannel->AudioDeviceChannelLeft);
850                AudioChannel* pDstR = pAudioOutputDevice->Channel(pEngineChannel->AudioDeviceChannelRight);
851                pEngineChannel->pChannelLeft->MixTo(pDstL, Samples);
852                pEngineChannel->pChannelRight->MixTo(pDstR, Samples);
853            }
854            // route FX send signal
855            {
856                for (int iFxSend = 0; iFxSend < pEngineChannel->GetFxSendCount(); iFxSend++) {
857                    FxSend* pFxSend = pEngineChannel->GetFxSend(iFxSend);
858                    // left channel
859                    const int iDstL = pFxSend->DestinationChannel(0);
860                    if (iDstL < 0) {
861                        dmsg(1,("Engine::RouteAudio() Error: invalid FX send (L) destination channel"));
862                    } else {
863                        AudioChannel* pDstL = pAudioOutputDevice->Channel(iDstL);
864                        if (!pDstL) {
865                            dmsg(1,("Engine::RouteAudio() Error: invalid FX send (L) destination channel"));
866                        } else pEngineChannel->pChannelLeft->MixTo(pDstL, Samples, pFxSend->Level());
867                    }
868                    // right channel
869                    const int iDstR = pFxSend->DestinationChannel(1);
870                    if (iDstR < 0) {
871                        dmsg(1,("Engine::RouteAudio() Error: invalid FX send (R) destination channel"));
872                    } else {
873                        AudioChannel* pDstR = pAudioOutputDevice->Channel(iDstR);
874                        if (!pDstR) {
875                            dmsg(1,("Engine::RouteAudio() Error: invalid FX send (R) destination channel"));
876                        } else pEngineChannel->pChannelRight->MixTo(pDstR, Samples, pFxSend->Level());
877                    }
878                }
879            }
880            // reset buffers with silence (zero out) for the next audio cycle
881            pEngineChannel->pChannelLeft->Clear();
882            pEngineChannel->pChannelRight->Clear();
883        }
884    
885        /**
886       * Free all keys which have turned inactive in this audio fragment, from       * Free all keys which have turned inactive in this audio fragment, from
887       * 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
888       * channel.       * channel.
# Line 599  namespace LinuxSampler { namespace gig { Line 959  namespace LinuxSampler { namespace gig {
959          if (pEngineChannel->GetMute()) return; // skip if sampler channel is muted          if (pEngineChannel->GetMute()) return; // skip if sampler channel is muted
960          #endif          #endif
961    
962            if (!pEngineChannel->pInstrument) return; // ignore if no instrument loaded
963    
964            //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
965            itNoteOnEvent->Param.Note.Key += pEngineChannel->GlobalTranspose;
966    
967          const int key = itNoteOnEvent->Param.Note.Key;          const int key = itNoteOnEvent->Param.Note.Key;
968            midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[key];
969    
970            // move note on event to the key's own event list
971            RTList<Event>::Iterator itNoteOnEventOnKeyList = itNoteOnEvent.moveToEndOf(pKey->pEvents);
972    
973            // if Solo Mode then kill all already active voices
974            if (pEngineChannel->SoloMode) {
975                Pool<uint>::Iterator itYoungestKey = pEngineChannel->pActiveKeys->last();
976                if (itYoungestKey) {
977                    const int iYoungestKey = *itYoungestKey;
978                    const midi_key_info_t* pOtherKey = &pEngineChannel->pMIDIKeyInfo[iYoungestKey];
979                    if (pOtherKey->Active) {
980                        // get final portamento position of currently active voice
981                        if (pEngineChannel->PortamentoMode) {
982                            RTList<Voice>::Iterator itVoice = pOtherKey->pActiveVoices->last();
983                            if (itVoice) itVoice->UpdatePortamentoPos(itNoteOnEventOnKeyList);
984                        }
985                        // kill all voices on the (other) key
986                        RTList<Voice>::Iterator itVoiceToBeKilled = pOtherKey->pActiveVoices->first();
987                        RTList<Voice>::Iterator end               = pOtherKey->pActiveVoices->end();
988                        for (; itVoiceToBeKilled != end; ++itVoiceToBeKilled) {
989                            if (itVoiceToBeKilled->Type != Voice::type_release_trigger)
990                                itVoiceToBeKilled->Kill(itNoteOnEventOnKeyList);
991                        }
992                    }
993                }
994                // set this key as 'currently active solo key'
995                pEngineChannel->SoloKey = key;
996            }
997    
998          // Change key dimension value if key is in keyswitching area          // Change key dimension value if key is in keyswitching area
999          {          {
1000              const ::gig::Instrument* pInstrument = pEngineChannel->pInstrument;              const ::gig::Instrument* pInstrument = pEngineChannel->pInstrument;
1001              if (key >= pInstrument->DimensionKeyRange.low && key <= pInstrument->DimensionKeyRange.high)              if (key >= pInstrument->DimensionKeyRange.low && key <= pInstrument->DimensionKeyRange.high)
1002                  pEngineChannel->CurrentKeyDimension = ((key - pInstrument->DimensionKeyRange.low) * 128) /                  pEngineChannel->CurrentKeyDimension = float(key - pInstrument->DimensionKeyRange.low) /
1003                      (pInstrument->DimensionKeyRange.high - pInstrument->DimensionKeyRange.low + 1);                      (pInstrument->DimensionKeyRange.high - pInstrument->DimensionKeyRange.low + 1);
1004          }          }
1005    
         midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[key];  
   
1006          pKey->KeyPressed = true; // the MIDI key was now pressed down          pKey->KeyPressed = true; // the MIDI key was now pressed down
1007          pKey->Velocity   = itNoteOnEvent->Param.Note.Velocity;          pKey->Velocity   = itNoteOnEventOnKeyList->Param.Note.Velocity;
1008          pKey->NoteOnTime = FrameTime + itNoteOnEvent->FragmentPos(); // will be used to calculate note length          pKey->NoteOnTime = FrameTime + itNoteOnEventOnKeyList->FragmentPos(); // will be used to calculate note length
1009    
1010          // cancel release process of voices on this key if needed          // cancel release process of voices on this key if needed
1011          if (pKey->Active && !pEngineChannel->SustainPedal) {          if (pKey->Active && !pEngineChannel->SustainPedal) {
1012              RTList<Event>::Iterator itCancelReleaseEvent = pKey->pEvents->allocAppend();              RTList<Event>::Iterator itCancelReleaseEvent = pKey->pEvents->allocAppend();
1013              if (itCancelReleaseEvent) {              if (itCancelReleaseEvent) {
1014                  *itCancelReleaseEvent = *itNoteOnEvent;                  // copy event                  *itCancelReleaseEvent = *itNoteOnEventOnKeyList;         // copy event
1015                  itCancelReleaseEvent->Type = Event::type_cancel_release; // transform event type                  itCancelReleaseEvent->Type = Event::type_cancel_release; // transform event type
1016              }              }
1017              else dmsg(1,("Event pool emtpy!\n"));              else dmsg(1,("Event pool emtpy!\n"));
1018          }          }
1019    
         // move note on event to the key's own event list  
         RTList<Event>::Iterator itNoteOnEventOnKeyList = itNoteOnEvent.moveToEndOf(pKey->pEvents);  
   
1020          // allocate and trigger new voice(s) for the key          // allocate and trigger new voice(s) for the key
1021          {          {
1022              // first, get total amount of required voices (dependant on amount of layers)              // first, get total amount of required voices (dependant on amount of layers)
1023              ::gig::Region* pRegion = pEngineChannel->pInstrument->GetRegion(itNoteOnEventOnKeyList->Param.Note.Key);              ::gig::Region* pRegion = pEngineChannel->pInstrument->GetRegion(itNoteOnEventOnKeyList->Param.Note.Key);
1024              if (pRegion) {              if (pRegion && !RegionSuspended(pRegion)) {
1025                  int voicesRequired = pRegion->Layers;                  int voicesRequired = pRegion->Layers;
1026                  // now launch the required amount of voices                  // now launch the required amount of voices
1027                  for (int i = 0; i < voicesRequired; i++)                  for (int i = 0; i < voicesRequired; i++)
# Line 644  namespace LinuxSampler { namespace gig { Line 1033  namespace LinuxSampler { namespace gig {
1033          if (!pKey->Active && !pKey->VoiceTheftsQueued)          if (!pKey->Active && !pKey->VoiceTheftsQueued)
1034              pKey->pEvents->free(itNoteOnEventOnKeyList);              pKey->pEvents->free(itNoteOnEventOnKeyList);
1035    
1036            if (!pEngineChannel->SoloMode || pEngineChannel->PortamentoPos < 0.0f) pEngineChannel->PortamentoPos = (float) key;
1037          pKey->RoundRobinIndex++;          pKey->RoundRobinIndex++;
1038      }      }
1039    
# Line 661  namespace LinuxSampler { namespace gig { Line 1051  namespace LinuxSampler { namespace gig {
1051          if (pEngineChannel->GetMute()) return; // skip if sampler channel is muted          if (pEngineChannel->GetMute()) return; // skip if sampler channel is muted
1052          #endif          #endif
1053    
1054          midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[itNoteOffEvent->Param.Note.Key];          //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
1055            itNoteOffEvent->Param.Note.Key += pEngineChannel->GlobalTranspose;
1056    
1057            const int iKey = itNoteOffEvent->Param.Note.Key;
1058            midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[iKey];
1059          pKey->KeyPressed = false; // the MIDI key was now released          pKey->KeyPressed = false; // the MIDI key was now released
1060    
1061          // release voices on this key if needed          // move event to the key's own event list
1062          if (pKey->Active && ShouldReleaseVoice(pEngineChannel, itNoteOffEvent->Param.Note.Key)) {          RTList<Event>::Iterator itNoteOffEventOnKeyList = itNoteOffEvent.moveToEndOf(pKey->pEvents);
1063              itNoteOffEvent->Type = Event::type_release; // transform event type  
1064            bool bShouldRelease = pKey->Active && ShouldReleaseVoice(pEngineChannel, itNoteOffEventOnKeyList->Param.Note.Key);
1065    
1066            // in case Solo Mode is enabled, kill all voices on this key and respawn a voice on the highest pressed key (if any)
1067            if (pEngineChannel->SoloMode && pEngineChannel->pInstrument) { //TODO: this feels like too much code just for handling solo mode :P
1068                bool bOtherKeysPressed = false;
1069                if (iKey == pEngineChannel->SoloKey) {
1070                    pEngineChannel->SoloKey = -1;
1071                    // if there's still a key pressed down, respawn a voice (group) on the highest key
1072                    for (int i = 127; i > 0; i--) {
1073                        midi_key_info_t* pOtherKey = &pEngineChannel->pMIDIKeyInfo[i];
1074                        if (pOtherKey->KeyPressed) {
1075                            bOtherKeysPressed = true;
1076                            // make the other key the new 'currently active solo key'
1077                            pEngineChannel->SoloKey = i;
1078                            // get final portamento position of currently active voice
1079                            if (pEngineChannel->PortamentoMode) {
1080                                RTList<Voice>::Iterator itVoice = pKey->pActiveVoices->first();
1081                                if (itVoice) itVoice->UpdatePortamentoPos(itNoteOffEventOnKeyList);
1082                            }
1083                            // create a pseudo note on event
1084                            RTList<Event>::Iterator itPseudoNoteOnEvent = pOtherKey->pEvents->allocAppend();
1085                            if (itPseudoNoteOnEvent) {
1086                                // copy event
1087                                *itPseudoNoteOnEvent = *itNoteOffEventOnKeyList;
1088                                // transform event to a note on event
1089                                itPseudoNoteOnEvent->Type                = Event::type_note_on;
1090                                itPseudoNoteOnEvent->Param.Note.Key      = i;
1091                                itPseudoNoteOnEvent->Param.Note.Velocity = pOtherKey->Velocity;
1092                                // allocate and trigger new voice(s) for the other key
1093                                {
1094                                    // first, get total amount of required voices (dependant on amount of layers)
1095                                    ::gig::Region* pRegion = pEngineChannel->pInstrument->GetRegion(i);
1096                                    if (pRegion) {
1097                                        int voicesRequired = pRegion->Layers;
1098                                        // now launch the required amount of voices
1099                                        for (int iLayer = 0; iLayer < voicesRequired; iLayer++)
1100                                            LaunchVoice(pEngineChannel, itPseudoNoteOnEvent, iLayer, false, true, false);
1101                                    }
1102                                }
1103                                // if neither a voice was spawned or postponed then remove note on event from key again
1104                                if (!pOtherKey->Active && !pOtherKey->VoiceTheftsQueued)
1105                                    pOtherKey->pEvents->free(itPseudoNoteOnEvent);
1106    
1107                            } else dmsg(1,("Could not respawn voice, no free event left\n"));
1108                            break; // done
1109                        }
1110                    }
1111                }
1112                if (bOtherKeysPressed) {
1113                    if (pKey->Active) { // kill all voices on this key
1114                        bShouldRelease = false; // no need to release, as we kill it here
1115                        RTList<Voice>::Iterator itVoiceToBeKilled = pKey->pActiveVoices->first();
1116                        RTList<Voice>::Iterator end               = pKey->pActiveVoices->end();
1117                        for (; itVoiceToBeKilled != end; ++itVoiceToBeKilled) {
1118                            if (itVoiceToBeKilled->Type != Voice::type_release_trigger)
1119                                itVoiceToBeKilled->Kill(itNoteOffEventOnKeyList);
1120                        }
1121                    }
1122                } else pEngineChannel->PortamentoPos = -1.0f;
1123            }
1124    
1125              // move event to the key's own event list          // if no solo mode (the usual case) or if solo mode and no other key pressed, then release voices on this key if needed
1126              RTList<Event>::Iterator itNoteOffEventOnKeyList = itNoteOffEvent.moveToEndOf(pKey->pEvents);          if (bShouldRelease) {
1127                itNoteOffEventOnKeyList->Type = Event::type_release; // transform event type
1128    
1129              // spawn release triggered voice(s) if needed              // spawn release triggered voice(s) if needed
1130              if (pKey->ReleaseTrigger) {              if (pKey->ReleaseTrigger && pEngineChannel->pInstrument) {
1131                  // first, get total amount of required voices (dependant on amount of layers)                  // first, get total amount of required voices (dependant on amount of layers)
1132                  ::gig::Region* pRegion = pEngineChannel->pInstrument->GetRegion(itNoteOffEventOnKeyList->Param.Note.Key);                  ::gig::Region* pRegion = pEngineChannel->pInstrument->GetRegion(itNoteOffEventOnKeyList->Param.Note.Key);
1133                  if (pRegion) {                  if (pRegion) {
# Line 687  namespace LinuxSampler { namespace gig { Line 1142  namespace LinuxSampler { namespace gig {
1142                  }                  }
1143                  pKey->ReleaseTrigger = false;                  pKey->ReleaseTrigger = false;
1144              }              }
   
             // if neither a voice was spawned or postponed then remove note off event from key again  
             if (!pKey->Active && !pKey->VoiceTheftsQueued)  
                 pKey->pEvents->free(itNoteOffEventOnKeyList);  
1145          }          }
1146    
1147            // if neither a voice was spawned or postponed on this key then remove note off event from key again
1148            if (!pKey->Active && !pKey->VoiceTheftsQueued)
1149                pKey->pEvents->free(itNoteOffEventOnKeyList);
1150      }      }
1151    
1152      /**      /**
# Line 775  namespace LinuxSampler { namespace gig { Line 1230  namespace LinuxSampler { namespace gig {
1230                      DimValues[i] = itNoteOnEvent->Param.Note.Velocity;                      DimValues[i] = itNoteOnEvent->Param.Note.Velocity;
1231                      break;                      break;
1232                  case ::gig::dimension_channelaftertouch:                  case ::gig::dimension_channelaftertouch:
1233                      DimValues[i] = 0; //TODO: we currently ignore this dimension                      DimValues[i] = pEngineChannel->ControllerTable[128];
1234                      break;                      break;
1235                  case ::gig::dimension_releasetrigger:                  case ::gig::dimension_releasetrigger:
1236                      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;
1237                      DimValues[i] = (uint) ReleaseTriggerVoice;                      DimValues[i] = (uint) ReleaseTriggerVoice;
1238                      break;                      break;
1239                  case ::gig::dimension_keyboard:                  case ::gig::dimension_keyboard:
1240                      DimValues[i] = (uint) pEngineChannel->CurrentKeyDimension;                      DimValues[i] = (uint) (pEngineChannel->CurrentKeyDimension * pRegion->pDimensionDefinitions[i].zones);
1241                      break;                      break;
1242                  case ::gig::dimension_roundrobin:                  case ::gig::dimension_roundrobin:
1243                      DimValues[i] = (uint) pEngineChannel->pMIDIKeyInfo[MIDIKey].RoundRobinIndex; // incremented for each note on                      DimValues[i] = (uint) pEngineChannel->pMIDIKeyInfo[MIDIKey].RoundRobinIndex; // incremented for each note on
# Line 867  namespace LinuxSampler { namespace gig { Line 1322  namespace LinuxSampler { namespace gig {
1322                      std::cerr << "gig::Engine::LaunchVoice() Error: Unknown dimension\n" << std::flush;                      std::cerr << "gig::Engine::LaunchVoice() Error: Unknown dimension\n" << std::flush;
1323              }              }
1324          }          }
1325    
1326            // return if this is a release triggered voice and there is no
1327            // releasetrigger dimension (could happen if an instrument
1328            // change has occured between note on and off)
1329            if (ReleaseTriggerVoice && VoiceType != Voice::type_release_trigger) return Pool<Voice>::Iterator();
1330    
1331          ::gig::DimensionRegion* pDimRgn = pRegion->GetDimensionRegionByValue(DimValues);          ::gig::DimensionRegion* pDimRgn = pRegion->GetDimensionRegionByValue(DimValues);
1332    
1333          // no need to continue if sample is silent          // no need to continue if sample is silent
# Line 1096  namespace LinuxSampler { namespace gig { Line 1557  namespace LinuxSampler { namespace gig {
1557    
1558              uint keygroup = itVoice->KeyGroup;              uint keygroup = itVoice->KeyGroup;
1559    
1560                // if the sample and dimension region belong to an
1561                // instrument that is unloaded, tell the disk thread to
1562                // release them
1563                if (itVoice->Orphan) {
1564                    pDiskThread->OrderDeletionOfDimreg(itVoice->pDimRgn);
1565                }
1566    
1567              // free the voice object              // free the voice object
1568              pVoicePool->free(itVoice);              pVoicePool->free(itVoice);
1569    
# Line 1140  namespace LinuxSampler { namespace gig { Line 1608  namespace LinuxSampler { namespace gig {
1608          // update controller value in the engine channel's controller table          // update controller value in the engine channel's controller table
1609          pEngineChannel->ControllerTable[itControlChangeEvent->Param.CC.Controller] = itControlChangeEvent->Param.CC.Value;          pEngineChannel->ControllerTable[itControlChangeEvent->Param.CC.Controller] = itControlChangeEvent->Param.CC.Value;
1610    
1611            // handle hard coded MIDI controllers
1612          switch (itControlChangeEvent->Param.CC.Controller) {          switch (itControlChangeEvent->Param.CC.Controller) {
1613                case 5: { // portamento time
1614                    pEngineChannel->PortamentoTime = (float) itControlChangeEvent->Param.CC.Value / 127.0f * (float) CONFIG_PORTAMENTO_TIME_MAX + (float) CONFIG_PORTAMENTO_TIME_MIN;
1615                    break;
1616                }
1617                case 6: { // data entry (currently only used for RPN controllers)
1618                    if (pEngineChannel->GetMidiRpnController() == 2) { // coarse tuning in half tones
1619                        int transpose = (int) itControlChangeEvent->Param.CC.Value - 64;
1620                        // limit to +- two octaves for now
1621                        transpose = RTMath::Min(transpose,  24);
1622                        transpose = RTMath::Max(transpose, -24);
1623                        pEngineChannel->GlobalTranspose = transpose;
1624                        // workaround, so we won't have hanging notes
1625                        ReleaseAllVoices(pEngineChannel, itControlChangeEvent);
1626                    }
1627                    // to avoid other MIDI CC #6 messages to be misenterpreted as RPN controller data
1628                    pEngineChannel->ResetMidiRpnController();
1629                    break;
1630                }
1631              case 7: { // volume              case 7: { // volume
1632                  //TODO: not sample accurate yet                  //TODO: not sample accurate yet
1633                  pEngineChannel->GlobalVolume = (float) itControlChangeEvent->Param.CC.Value / 127.0f *  CONFIG_GLOBAL_ATTENUATION;                  pEngineChannel->MidiVolume = VolumeCurve[itControlChangeEvent->Param.CC.Value];
1634                  pEngineChannel->bStatusChanged = true; // engine channel status has changed, so set notify flag                  pEngineChannel->bStatusChanged = true; // engine channel status has changed, so set notify flag
1635                  break;                  break;
1636              }              }
1637              case 10: { // panpot              case 10: { // panpot
1638                  //TODO: not sample accurate yet                  //TODO: not sample accurate yet
1639                  const int pan = (int) itControlChangeEvent->Param.CC.Value - 64;                  pEngineChannel->GlobalPanLeft  = PanCurve[128 - itControlChangeEvent->Param.CC.Value];
1640                  pEngineChannel->GlobalPanLeft  = 1.0f - float(RTMath::Max(pan, 0)) /  63.0f;                  pEngineChannel->GlobalPanRight = PanCurve[itControlChangeEvent->Param.CC.Value];
                 pEngineChannel->GlobalPanRight = 1.0f - float(RTMath::Min(pan, 0)) / -64.0f;  
1641                  break;                  break;
1642              }              }
1643              case 64: { // sustain              case 64: { // sustain
# Line 1201  namespace LinuxSampler { namespace gig { Line 1687  namespace LinuxSampler { namespace gig {
1687                  }                  }
1688                  break;                  break;
1689              }              }
1690                case 65: { // portamento on / off
1691                    const bool bPortamento = itControlChangeEvent->Param.CC.Value >= 64;
1692                    if (bPortamento != pEngineChannel->PortamentoMode)
1693                        KillAllVoices(pEngineChannel, itControlChangeEvent);
1694                    pEngineChannel->PortamentoMode = bPortamento;
1695                    break;
1696                }
1697              case 66: { // sostenuto              case 66: { // sostenuto
1698                  if (itControlChangeEvent->Param.CC.Value >= 64 && !pEngineChannel->SostenutoPedal) {                  if (itControlChangeEvent->Param.CC.Value >= 64 && !pEngineChannel->SostenutoPedal) {
1699                      dmsg(4,("SOSTENUTO (CENTER) PEDAL DOWN\n"));                      dmsg(4,("SOSTENUTO (CENTER) PEDAL DOWN\n"));
# Line 1241  namespace LinuxSampler { namespace gig { Line 1734  namespace LinuxSampler { namespace gig {
1734                  }                  }
1735                  break;                  break;
1736              }              }
1737                case 100: { // RPN controller LSB
1738                    pEngineChannel->SetMidiRpnControllerLsb(itControlChangeEvent->Param.CC.Value);
1739                    break;
1740                }
1741                case 101: { // RPN controller MSB
1742                    pEngineChannel->SetMidiRpnControllerMsb(itControlChangeEvent->Param.CC.Value);
1743                    break;
1744                }
1745    
1746    
1747              // Channel Mode Messages              // Channel Mode Messages
# Line 1254  namespace LinuxSampler { namespace gig { Line 1755  namespace LinuxSampler { namespace gig {
1755                  break;                  break;
1756              }              }
1757              case 123: { // all notes off              case 123: { // all notes off
1758                    #if CONFIG_PROCESS_ALL_NOTES_OFF
1759                  ReleaseAllVoices(pEngineChannel, itControlChangeEvent);                  ReleaseAllVoices(pEngineChannel, itControlChangeEvent);
1760                    #endif // CONFIG_PROCESS_ALL_NOTES_OFF
1761                    break;
1762                }
1763                case 126: { // mono mode on
1764                    if (!pEngineChannel->SoloMode)
1765                        KillAllVoices(pEngineChannel, itControlChangeEvent);
1766                    pEngineChannel->SoloMode = true;
1767                    break;
1768                }
1769                case 127: { // poly mode on
1770                    if (pEngineChannel->SoloMode)
1771                        KillAllVoices(pEngineChannel, itControlChangeEvent);
1772                    pEngineChannel->SoloMode = false;
1773                  break;                  break;
1774              }              }
1775          }          }
1776    
1777            // handle FX send controllers
1778            if (!pEngineChannel->fxSends.empty()) {
1779                for (int iFxSend = 0; iFxSend < pEngineChannel->GetFxSendCount(); iFxSend++) {
1780                    FxSend* pFxSend = pEngineChannel->GetFxSend(iFxSend);
1781                    if (pFxSend->MidiController() == itControlChangeEvent->Param.CC.Controller)
1782                        pFxSend->SetLevel(itControlChangeEvent->Param.CC.Value);
1783                        pFxSend->SetInfoChanged(true);
1784                }
1785            }
1786      }      }
1787    
1788      /**      /**
# Line 1266  namespace LinuxSampler { namespace gig { Line 1791  namespace LinuxSampler { namespace gig {
1791       *  @param itSysexEvent - sysex data size and time stamp of the sysex event       *  @param itSysexEvent - sysex data size and time stamp of the sysex event
1792       */       */
1793      void Engine::ProcessSysex(Pool<Event>::Iterator& itSysexEvent) {      void Engine::ProcessSysex(Pool<Event>::Iterator& itSysexEvent) {
1794          RingBuffer<uint8_t>::NonVolatileReader reader = pSysexBuffer->get_non_volatile_reader();          RingBuffer<uint8_t,false>::NonVolatileReader reader = pSysexBuffer->get_non_volatile_reader();
1795    
1796          uint8_t exclusive_status, id;          uint8_t exclusive_status, id;
1797          if (!reader.pop(&exclusive_status)) goto free_sysex_data;          if (!reader.pop(&exclusive_status)) goto free_sysex_data;
# Line 1285  namespace LinuxSampler { namespace gig { Line 1810  namespace LinuxSampler { namespace gig {
1810    
1811                  // command address                  // command address
1812                  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)
1813                  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
1814                  if (reader.read(&addr[0], 3) != 3) goto free_sysex_data;                  if (reader.read(&addr[0], 3) != 3) goto free_sysex_data;
1815                  if (addr[0] == 0x40 && addr[1] == 0x00) { // System Parameters                  if (addr[0] == 0x40 && addr[1] == 0x00) { // System Parameters
1816                      dmsg(3,("\tSystem Parameter\n"));                      dmsg(3,("\tSystem Parameter\n"));
# Line 1332  namespace LinuxSampler { namespace gig { Line 1857  namespace LinuxSampler { namespace gig {
1857       *                     question       *                     question
1858       * @param DataSize   - size of the GS message data (in bytes)       * @param DataSize   - size of the GS message data (in bytes)
1859       */       */
1860      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) {
1861          RingBuffer<uint8_t>::NonVolatileReader reader = AddrReader;          RingBuffer<uint8_t,false>::NonVolatileReader reader = AddrReader;
1862          uint bytes = 3 /*addr*/ + DataSize;          uint bytes = 3 /*addr*/ + DataSize;
1863          uint8_t addr_and_data[bytes];          uint8_t addr_and_data[bytes];
1864          reader.read(&addr_and_data[0], bytes);          reader.read(&addr_and_data[0], bytes);
# Line 1448  namespace LinuxSampler { namespace gig { Line 1973  namespace LinuxSampler { namespace gig {
1973      }      }
1974    
1975      String Engine::Description() {      String Engine::Description() {
1976          return "Gigasampler Engine";          return "Gigasampler Format Engine";
1977      }      }
1978    
1979      String Engine::Version() {      String Engine::Version() {
1980          String s = "$Revision: 1.56 $";          String s = "$Revision: 1.86 $";
1981          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
1982      }      }
1983    
1984        InstrumentManager* Engine::GetInstrumentManager() {
1985            return &instruments;
1986        }
1987    
1988        // static constant initializers
1989        const Engine::FloatTable Engine::VolumeCurve(InitVolumeCurve());
1990        const Engine::FloatTable Engine::PanCurve(InitPanCurve());
1991        const Engine::FloatTable Engine::CrossfadeCurve(InitCrossfadeCurve());
1992    
1993        float* Engine::InitVolumeCurve() {
1994            // line-segment approximation
1995            const float segments[] = {
1996                0, 0, 2, 0.0046, 16, 0.016, 31, 0.051, 45, 0.115, 54.5, 0.2,
1997                64.5, 0.39, 74, 0.74, 92, 1.03, 114, 1.94, 119.2, 2.2, 127, 2.2
1998            };
1999            return InitCurve(segments);
2000        }
2001    
2002        float* Engine::InitPanCurve() {
2003            // line-segment approximation
2004            const float segments[] = {
2005                0, 0, 1, 0,
2006                2, 0.05, 31.5, 0.7, 51, 0.851, 74.5, 1.12,
2007                127, 1.41, 128, 1.41
2008            };
2009            return InitCurve(segments, 129);
2010        }
2011    
2012        float* Engine::InitCrossfadeCurve() {
2013            // line-segment approximation
2014            const float segments[] = {
2015                0, 0, 1, 0.03, 10, 0.1, 51, 0.58, 127, 1
2016            };
2017            return InitCurve(segments);
2018        }
2019    
2020        float* Engine::InitCurve(const float* segments, int size) {
2021            float* y = new float[size];
2022            for (int x = 0 ; x < size ; x++) {
2023                if (x > segments[2]) segments += 2;
2024                y[x] = segments[1] + (x - segments[0]) *
2025                    (segments[3] - segments[1]) / (segments[2] - segments[0]);
2026            }
2027            return y;
2028        }
2029    
2030        /**
2031         * Changes the instrument for an engine channel.
2032         *
2033         * @param pEngineChannel - engine channel on which the instrument
2034         *                         should be changed
2035         * @param pInstrument - new instrument
2036         * @returns a list of dimension regions from the old instrument
2037         *          that are still in use
2038         */
2039        ::gig::DimensionRegion** Engine::ChangeInstrument(EngineChannel* pEngineChannel, ::gig::Instrument* pInstrument) {
2040            instrument_change_command_t command;
2041            command.pEngineChannel = pEngineChannel;
2042            command.pInstrument = pInstrument;
2043            InstrumentChangeQueue->push(&command);
2044    
2045            // wait for the audio thread to confirm that the instrument
2046            // change has been done
2047            instrument_change_reply_t reply;
2048            while (InstrumentChangeReplyQueue->pop(&reply) == 0) {
2049                usleep(10000);
2050            }
2051            return pDimRegionsInUse;
2052        }
2053    
2054  }} // namespace LinuxSampler::gig  }} // namespace LinuxSampler::gig

Legend:
Removed from v.781  
changed lines
  Added in v.1644

  ViewVC Help
Powered by ViewVC