/[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 1764 by persson, Sat Sep 6 14:55:18 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 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      /**      /**
127       * Destructor       * Destructor
128       */       */
129      Engine::~Engine() {      Engine::~Engine() {
130            MidiInputPort::RemoveSysexListener(this);
131          if (pDiskThread) {          if (pDiskThread) {
132              dmsg(1,("Stopping disk thread..."));              dmsg(1,("Stopping disk thread..."));
133              pDiskThread->StopThread();              pDiskThread->StopThread();
# Line 131  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 140  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 153  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 165  namespace LinuxSampler { namespace gig { Line 283  namespace LinuxSampler { namespace gig {
283    
284      /**      /**
285       *  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
286       *  control and status variables. This method is not thread safe!       *  control and status variables. This method is protected by a mutex.
287       */       */
288      void Engine::ResetInternal() {      void Engine::ResetInternal() {
289            ResetInternalMutex.Lock();
290    
291            // make sure that the engine does not get any sysex messages
292            // while it's reseting
293            bool sysexDisabled = MidiInputPort::RemoveSysexListener(this);
294          ActiveVoiceCount    = 0;          ActiveVoiceCount    = 0;
295          ActiveVoiceCountMax = 0;          ActiveVoiceCountMax = 0;
296    
# Line 191  namespace LinuxSampler { namespace gig { Line 314  namespace LinuxSampler { namespace gig {
314          // delete all input events          // delete all input events
315          pEventQueue->init();          pEventQueue->init();
316          pSysexBuffer->init();          pSysexBuffer->init();
317            if (sysexDisabled) MidiInputPort::AddSysexListener(this);
318            ResetInternalMutex.Unlock();
319      }      }
320    
321      /**      /**
# Line 200  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 220  namespace LinuxSampler { namespace gig { Line 352  namespace LinuxSampler { namespace gig {
352          }          }
353          catch (AudioOutputException e) {          catch (AudioOutputException e) {
354              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();
355              throw LinuxSamplerException(msg);              throw Exception(msg);
356          }          }
357    
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 249  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 278  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 298  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 321  namespace LinuxSampler { namespace gig { Line 557  namespace LinuxSampler { namespace gig {
557      }      }
558    
559      /**      /**
560       *  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.
561       *  calculated audio data of all voices of this engine will be placed into       * The engine will iterate through all engine channels and render audio
562       *  the engine's audio sum buffer which has to be copied and eventually be       * for each engine channel independently. The calculated audio data of
563       *  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
564       *  AlsaIO or JackIO) right after.       * buffers of the respective audio output device, connected to the
565         * respective engine channel.
566       *       *
567       *  @param Samples - number of sample points to be rendered       *  @param Samples - number of sample points to be rendered
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()) {
575              dmsg(5,("gig::Engine: engine disabled (val=%d)\n",EngineDisabled.GetUnsafe()));              dmsg(5,("gig::Engine: engine disabled (val=%d)\n",EngineDisabled.GetUnsafe()));
576                EngineDisabled.RttDone();
577              return 0;              return 0;
578          }          }
579    
580            // process requests for suspending / resuming regions (i.e. to avoid
581            // crashes while these regions are modified by an instrument editor)
582            ProcessSuspensionsChanges();
583    
584          // 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)
585          pEventGenerator->UpdateFragmentTime(Samples);          pEventGenerator->UpdateFragmentTime(Samples);
586    
# Line 368  namespace LinuxSampler { namespace gig { Line 610  namespace LinuxSampler { namespace gig {
610          // reset internal voice counter (just for statistic of active voices)          // reset internal voice counter (just for statistic of active voices)
611          ActiveVoiceCountTemp = 0;          ActiveVoiceCountTemp = 0;
612    
613            // handle instrument change commands
614            bool instrumentChanged = false;
615            for (int i = 0; i < engineChannels.size(); i++) {
616                EngineChannel* pEngineChannel = engineChannels[i];
617    
618                // as we're going to (carefully) write some status to the
619                // synchronized struct, we cast away the const
620                EngineChannel::instrument_change_command_t& cmd =
621                    const_cast<EngineChannel::instrument_change_command_t&>(pEngineChannel->InstrumentChangeCommandReader.Lock());
622    
623                pEngineChannel->pDimRegionsInUse = cmd.pDimRegionsInUse;
624                pEngineChannel->pDimRegionsInUse->clear();
625    
626                if (cmd.bChangeInstrument) {
627                    // change instrument
628                    dmsg(5,("Engine: instrument change command received\n"));
629                    cmd.bChangeInstrument = false;
630                    pEngineChannel->pInstrument = cmd.pInstrument;
631                    instrumentChanged = true;
632    
633                    // Iterate through all active voices and mark them as
634                    // "orphans", which means that the dimension regions
635                    // and samples they use should be released to the
636                    // instrument resource manager when the voices die.
637                    int i = 0;
638                    RTList<uint>::Iterator iuiKey = pEngineChannel->pActiveKeys->first();
639                    RTList<uint>::Iterator end    = pEngineChannel->pActiveKeys->end();
640                    while (iuiKey != end) { // iterate through all active keys
641                        midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[*iuiKey];
642                        ++iuiKey;
643    
644                        RTList<Voice>::Iterator itVoice     = pKey->pActiveVoices->first();
645                        RTList<Voice>::Iterator itVoicesEnd = pKey->pActiveVoices->end();
646                        for (; itVoice != itVoicesEnd; ++itVoice) { // iterate through all voices on this key
647                            itVoice->Orphan = true;
648                        }
649                    }
650                }
651            }
652            if (instrumentChanged) {
653                //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
654                ResetSuspendedRegions();
655            }
656    
657          // handle events on all engine channels          // handle events on all engine channels
658          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  
659              ProcessEvents(engineChannels[i], Samples);              ProcessEvents(engineChannels[i], Samples);
660          }          }
661    
662          // render all 'normal', active voices on all engine channels          // render all 'normal', active voices on all engine channels
663          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  
664              RenderActiveVoices(engineChannels[i], Samples);              RenderActiveVoices(engineChannels[i], Samples);
665          }          }
666    
667          // 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
668          RenderStolenVoices(Samples);          RenderStolenVoices(Samples);
669    
670            // handle audio routing for engine channels with FX sends
671            for (int i = 0; i < engineChannels.size(); i++) {
672                if (engineChannels[i]->fxSends.empty()) continue; // ignore if no FX sends
673                RouteAudio(engineChannels[i], Samples);
674            }
675    
676          // handle cleanup on all engine channels for the next audio fragment          // handle cleanup on all engine channels for the next audio fragment
677          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  
678              PostProcess(engineChannels[i]);              PostProcess(engineChannels[i]);
679          }          }
680    
# Line 400  namespace LinuxSampler { namespace gig { Line 689  namespace LinuxSampler { namespace gig {
689          ActiveVoiceCount = ActiveVoiceCountTemp;          ActiveVoiceCount = ActiveVoiceCountTemp;
690          if (ActiveVoiceCount > ActiveVoiceCountMax) ActiveVoiceCountMax = ActiveVoiceCount;          if (ActiveVoiceCount > ActiveVoiceCountMax) ActiveVoiceCountMax = ActiveVoiceCount;
691    
692            // in case regions were previously suspended and we killed voices
693            // with disk streams due to that, check if those streams have finally
694            // been deleted by the disk thread
695            if (iPendingStreamDeletions) ProcessPendingStreamDeletions();
696    
697            for (int i = 0; i < engineChannels.size(); i++) {
698                engineChannels[i]->InstrumentChangeCommandReader.Unlock();
699            }
700          FrameTime += Samples;          FrameTime += Samples;
701    
702            EngineDisabled.RttDone();
703          return 0;          return 0;
704      }      }
705    
# Line 467  namespace LinuxSampler { namespace gig { Line 765  namespace LinuxSampler { namespace gig {
765          if (pEngineChannel->GetMute()) return; // skip if sampler channel is muted          if (pEngineChannel->GetMute()) return; // skip if sampler channel is muted
766          #endif          #endif
767    
768            uint voiceCount = 0;
769            uint streamCount = 0;
770          RTList<uint>::Iterator iuiKey = pEngineChannel->pActiveKeys->first();          RTList<uint>::Iterator iuiKey = pEngineChannel->pActiveKeys->first();
771          RTList<uint>::Iterator end    = pEngineChannel->pActiveKeys->end();          RTList<uint>::Iterator end    = pEngineChannel->pActiveKeys->end();
772          while (iuiKey != end) { // iterate through all active keys          while (iuiKey != end) { // iterate through all active keys
# Line 478  namespace LinuxSampler { namespace gig { Line 778  namespace LinuxSampler { namespace gig {
778              for (; itVoice != itVoicesEnd; ++itVoice) { // iterate through all voices on this key              for (; itVoice != itVoicesEnd; ++itVoice) { // iterate through all voices on this key
779                  // now render current voice                  // now render current voice
780                  itVoice->Render(Samples);                  itVoice->Render(Samples);
781                  if (itVoice->IsActive()) ActiveVoiceCountTemp++; // still active                  if (itVoice->IsActive()) { // still active
782                  else { // voice reached end, is now inactive                      if (!itVoice->Orphan) {
783                            *(pEngineChannel->pDimRegionsInUse->allocAppend()) = itVoice->pDimRgn;
784                        }
785                        ActiveVoiceCountTemp++;
786                        voiceCount++;
787    
788                        if (itVoice->PlaybackState == Voice::playback_state_disk) {
789                            if ((itVoice->DiskStreamRef).State == Stream::state_active) streamCount++;
790                        }
791                    }  else { // voice reached end, is now inactive
792                      FreeVoice(pEngineChannel, itVoice); // remove voice from the list of active voices                      FreeVoice(pEngineChannel, itVoice); // remove voice from the list of active voices
793                  }                  }
794              }              }
795          }          }
796    
797            pEngineChannel->SetVoiceCount(voiceCount);
798            pEngineChannel->SetDiskStreamCount(streamCount);
799      }      }
800    
801      /**      /**
# Line 504  namespace LinuxSampler { namespace gig { Line 816  namespace LinuxSampler { namespace gig {
816          RTList<Event>::Iterator end               = pVoiceStealingQueue->end();          RTList<Event>::Iterator end               = pVoiceStealingQueue->end();
817          for (; itVoiceStealEvent != end; ++itVoiceStealEvent) {          for (; itVoiceStealEvent != end; ++itVoiceStealEvent) {
818              EngineChannel* pEngineChannel = (EngineChannel*) itVoiceStealEvent->pEngineChannel;              EngineChannel* pEngineChannel = (EngineChannel*) itVoiceStealEvent->pEngineChannel;
819                if (!pEngineChannel->pInstrument) continue; // ignore if no instrument loaded
820              Pool<Voice>::Iterator itNewVoice =              Pool<Voice>::Iterator itNewVoice =
821                  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);
822              if (itNewVoice) {              if (itNewVoice) {
823                  itNewVoice->Render(Samples);                  itNewVoice->Render(Samples);
824                  if (itNewVoice->IsActive()) ActiveVoiceCountTemp++; // still active                  if (itNewVoice->IsActive()) { // still active
825                  else { // voice reached end, is now inactive                      *(pEngineChannel->pDimRegionsInUse->allocAppend()) = itNewVoice->pDimRgn;
826                        ActiveVoiceCountTemp++;
827                        pEngineChannel->SetVoiceCount(pEngineChannel->GetVoiceCount() + 1);
828    
829                        if (itNewVoice->PlaybackState == Voice::playback_state_disk) {
830                            if (itNewVoice->DiskStreamRef.State == Stream::state_active) {
831                                pEngineChannel->SetDiskStreamCount(pEngineChannel->GetDiskStreamCount() + 1);
832                            }
833                        }
834                    } else { // voice reached end, is now inactive
835                      FreeVoice(pEngineChannel, itNewVoice); // remove voice from the list of active voices                      FreeVoice(pEngineChannel, itNewVoice); // remove voice from the list of active voices
836                  }                  }
837              }              }
# Line 523  namespace LinuxSampler { namespace gig { Line 845  namespace LinuxSampler { namespace gig {
845      }      }
846    
847      /**      /**
848         * Will be called in case the respective engine channel sports FX send
849         * channels. In this particular case, engine channel local buffers are
850         * used to render and mix all voices to. This method is responsible for
851         * copying the audio data from those local buffers to the master audio
852         * output channels as well as to the FX send audio output channels with
853         * their respective FX send levels.
854         *
855         * @param pEngineChannel - engine channel from which audio should be
856         *                         routed
857         * @param Samples        - amount of sample points to be routed in
858         *                         this audio fragment cycle
859         */
860        void Engine::RouteAudio(EngineChannel* pEngineChannel, uint Samples) {
861            // route dry signal
862            {
863                AudioChannel* pDstL = pAudioOutputDevice->Channel(pEngineChannel->AudioDeviceChannelLeft);
864                AudioChannel* pDstR = pAudioOutputDevice->Channel(pEngineChannel->AudioDeviceChannelRight);
865                pEngineChannel->pChannelLeft->MixTo(pDstL, Samples);
866                pEngineChannel->pChannelRight->MixTo(pDstR, Samples);
867            }
868            // route FX send signal
869            {
870                for (int iFxSend = 0; iFxSend < pEngineChannel->GetFxSendCount(); iFxSend++) {
871                    FxSend* pFxSend = pEngineChannel->GetFxSend(iFxSend);
872                    for (int iChan = 0; iChan < 2; ++iChan) {
873                        AudioChannel* pSource =
874                            (iChan)
875                                ? pEngineChannel->pChannelRight
876                                : pEngineChannel->pChannelLeft;
877                        const int iDstChan = pFxSend->DestinationChannel(iChan);
878                        if (iDstChan < 0) {
879                            dmsg(1,("Engine::RouteAudio() Error: invalid FX send (%s) destination channel (%d->%d)", ((iChan) ? "R" : "L"), iChan, iDstChan));
880                            goto channel_cleanup;
881                        }
882                        AudioChannel* pDstChan = NULL;
883                        if (pFxSend->DestinationMasterEffectChain() >= 0) { // fx send routed to an internal master effect
884                            EffectChain* pEffectChain =
885                                pAudioOutputDevice->MasterEffectChain(
886                                    pFxSend->DestinationMasterEffectChain()
887                                );
888                            if (!pEffectChain) {
889                                dmsg(1,("Engine::RouteAudio() Error: invalid FX send (%s) destination effect chain %d", ((iChan) ? "R" : "L"), pFxSend->DestinationMasterEffectChain()));
890                                goto channel_cleanup;
891                            }
892                            Effect* pEffect =
893                                pEffectChain->GetEffect(
894                                    pFxSend->DestinationMasterEffect()
895                                );
896                            if (!pEffect) {
897                                dmsg(1,("Engine::RouteAudio() Error: invalid FX send (%s) destination effect %d of effect chain %d", ((iChan) ? "R" : "L"), pFxSend->DestinationMasterEffect(), pFxSend->DestinationMasterEffectChain()));
898                                goto channel_cleanup;
899                            }
900                            pDstChan = pEffect->InputChannel(iDstChan);
901                        } else { // FX send routed directly to an audio output channel
902                            pDstChan = pAudioOutputDevice->Channel(iDstChan);
903                        }
904                        if (!pDstChan) {
905                            dmsg(1,("Engine::RouteAudio() Error: invalid FX send (%s) destination channel (%d->%d)", ((iChan) ? "R" : "L"), iChan, iDstChan));
906                            goto channel_cleanup;
907                        }
908                        pSource->MixTo(pDstChan, Samples, pFxSend->Level());
909                    }
910                }
911            }
912            channel_cleanup:
913            // reset buffers with silence (zero out) for the next audio cycle
914            pEngineChannel->pChannelLeft->Clear();
915            pEngineChannel->pChannelRight->Clear();
916        }
917    
918        /**
919       * Free all keys which have turned inactive in this audio fragment, from       * Free all keys which have turned inactive in this audio fragment, from
920       * 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
921       * channel.       * channel.
# Line 562  namespace LinuxSampler { namespace gig { Line 955  namespace LinuxSampler { namespace gig {
955       *       *
956       *  @param pData - pointer to sysex data       *  @param pData - pointer to sysex data
957       *  @param Size  - lenght of sysex data (in bytes)       *  @param Size  - lenght of sysex data (in bytes)
958         *  @param pSender - the MIDI input port on which the SysEx message was
959         *                   received
960       */       */
961      void Engine::SendSysex(void* pData, uint Size) {      void Engine::SendSysex(void* pData, uint Size, MidiInputPort* pSender) {
962          Event event             = pEventGenerator->CreateEvent();          Event event             = pEventGenerator->CreateEvent();
963          event.Type              = Event::type_sysex;          event.Type              = Event::type_sysex;
964          event.Param.Sysex.Size  = Size;          event.Param.Sysex.Size  = Size;
965          event.pEngineChannel    = NULL; // as Engine global event          event.pEngineChannel    = NULL; // as Engine global event
966            event.pMidiInputPort    = pSender;
967          if (pEventQueue->write_space() > 0) {          if (pEventQueue->write_space() > 0) {
968              if (pSysexBuffer->write_space() >= Size) {              if (pSysexBuffer->write_space() >= Size) {
969                  // copy sysex data to input buffer                  // copy sysex data to input buffer
# Line 599  namespace LinuxSampler { namespace gig { Line 995  namespace LinuxSampler { namespace gig {
995          if (pEngineChannel->GetMute()) return; // skip if sampler channel is muted          if (pEngineChannel->GetMute()) return; // skip if sampler channel is muted
996          #endif          #endif
997    
998            if (!pEngineChannel->pInstrument) return; // ignore if no instrument loaded
999    
1000            //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
1001            itNoteOnEvent->Param.Note.Key += pEngineChannel->GlobalTranspose;
1002    
1003          const int key = itNoteOnEvent->Param.Note.Key;          const int key = itNoteOnEvent->Param.Note.Key;
1004            midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[key];
1005    
1006            // move note on event to the key's own event list
1007            RTList<Event>::Iterator itNoteOnEventOnKeyList = itNoteOnEvent.moveToEndOf(pKey->pEvents);
1008    
1009            // if Solo Mode then kill all already active voices
1010            if (pEngineChannel->SoloMode) {
1011                Pool<uint>::Iterator itYoungestKey = pEngineChannel->pActiveKeys->last();
1012                if (itYoungestKey) {
1013                    const int iYoungestKey = *itYoungestKey;
1014                    const midi_key_info_t* pOtherKey = &pEngineChannel->pMIDIKeyInfo[iYoungestKey];
1015                    if (pOtherKey->Active) {
1016                        // get final portamento position of currently active voice
1017                        if (pEngineChannel->PortamentoMode) {
1018                            RTList<Voice>::Iterator itVoice = pOtherKey->pActiveVoices->last();
1019                            if (itVoice) itVoice->UpdatePortamentoPos(itNoteOnEventOnKeyList);
1020                        }
1021                        // kill all voices on the (other) key
1022                        RTList<Voice>::Iterator itVoiceToBeKilled = pOtherKey->pActiveVoices->first();
1023                        RTList<Voice>::Iterator end               = pOtherKey->pActiveVoices->end();
1024                        for (; itVoiceToBeKilled != end; ++itVoiceToBeKilled) {
1025                            if (itVoiceToBeKilled->Type != Voice::type_release_trigger)
1026                                itVoiceToBeKilled->Kill(itNoteOnEventOnKeyList);
1027                        }
1028                    }
1029                }
1030                // set this key as 'currently active solo key'
1031                pEngineChannel->SoloKey = key;
1032            }
1033    
1034          // Change key dimension value if key is in keyswitching area          // Change key dimension value if key is in keyswitching area
1035          {          {
1036              const ::gig::Instrument* pInstrument = pEngineChannel->pInstrument;              const ::gig::Instrument* pInstrument = pEngineChannel->pInstrument;
1037              if (key >= pInstrument->DimensionKeyRange.low && key <= pInstrument->DimensionKeyRange.high)              if (key >= pInstrument->DimensionKeyRange.low && key <= pInstrument->DimensionKeyRange.high)
1038                  pEngineChannel->CurrentKeyDimension = ((key - pInstrument->DimensionKeyRange.low) * 128) /                  pEngineChannel->CurrentKeyDimension = float(key - pInstrument->DimensionKeyRange.low) /
1039                      (pInstrument->DimensionKeyRange.high - pInstrument->DimensionKeyRange.low + 1);                      (pInstrument->DimensionKeyRange.high - pInstrument->DimensionKeyRange.low + 1);
1040          }          }
1041    
         midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[key];  
   
1042          pKey->KeyPressed = true; // the MIDI key was now pressed down          pKey->KeyPressed = true; // the MIDI key was now pressed down
1043          pKey->Velocity   = itNoteOnEvent->Param.Note.Velocity;          pKey->Velocity   = itNoteOnEventOnKeyList->Param.Note.Velocity;
1044          pKey->NoteOnTime = FrameTime + itNoteOnEvent->FragmentPos(); // will be used to calculate note length          pKey->NoteOnTime = FrameTime + itNoteOnEventOnKeyList->FragmentPos(); // will be used to calculate note length
1045    
1046          // cancel release process of voices on this key if needed          // cancel release process of voices on this key if needed
1047          if (pKey->Active && !pEngineChannel->SustainPedal) {          if (pKey->Active && !pEngineChannel->SustainPedal) {
1048              RTList<Event>::Iterator itCancelReleaseEvent = pKey->pEvents->allocAppend();              RTList<Event>::Iterator itCancelReleaseEvent = pKey->pEvents->allocAppend();
1049              if (itCancelReleaseEvent) {              if (itCancelReleaseEvent) {
1050                  *itCancelReleaseEvent = *itNoteOnEvent;                  // copy event                  *itCancelReleaseEvent = *itNoteOnEventOnKeyList;         // copy event
1051                  itCancelReleaseEvent->Type = Event::type_cancel_release; // transform event type                  itCancelReleaseEvent->Type = Event::type_cancel_release; // transform event type
1052              }              }
1053              else dmsg(1,("Event pool emtpy!\n"));              else dmsg(1,("Event pool emtpy!\n"));
1054          }          }
1055    
         // move note on event to the key's own event list  
         RTList<Event>::Iterator itNoteOnEventOnKeyList = itNoteOnEvent.moveToEndOf(pKey->pEvents);  
   
1056          // allocate and trigger new voice(s) for the key          // allocate and trigger new voice(s) for the key
1057          {          {
1058              // first, get total amount of required voices (dependant on amount of layers)              // first, get total amount of required voices (dependant on amount of layers)
1059              ::gig::Region* pRegion = pEngineChannel->pInstrument->GetRegion(itNoteOnEventOnKeyList->Param.Note.Key);              ::gig::Region* pRegion = pEngineChannel->pInstrument->GetRegion(itNoteOnEventOnKeyList->Param.Note.Key);
1060              if (pRegion) {              if (pRegion && !RegionSuspended(pRegion)) {
1061                  int voicesRequired = pRegion->Layers;                  int voicesRequired = pRegion->Layers;
1062                  // now launch the required amount of voices                  // now launch the required amount of voices
1063                  for (int i = 0; i < voicesRequired; i++)                  for (int i = 0; i < voicesRequired; i++)
# Line 644  namespace LinuxSampler { namespace gig { Line 1069  namespace LinuxSampler { namespace gig {
1069          if (!pKey->Active && !pKey->VoiceTheftsQueued)          if (!pKey->Active && !pKey->VoiceTheftsQueued)
1070              pKey->pEvents->free(itNoteOnEventOnKeyList);              pKey->pEvents->free(itNoteOnEventOnKeyList);
1071    
1072            if (!pEngineChannel->SoloMode || pEngineChannel->PortamentoPos < 0.0f) pEngineChannel->PortamentoPos = (float) key;
1073          pKey->RoundRobinIndex++;          pKey->RoundRobinIndex++;
1074      }      }
1075    
# Line 661  namespace LinuxSampler { namespace gig { Line 1087  namespace LinuxSampler { namespace gig {
1087          if (pEngineChannel->GetMute()) return; // skip if sampler channel is muted          if (pEngineChannel->GetMute()) return; // skip if sampler channel is muted
1088          #endif          #endif
1089    
1090          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
1091            itNoteOffEvent->Param.Note.Key += pEngineChannel->GlobalTranspose;
1092    
1093            const int iKey = itNoteOffEvent->Param.Note.Key;
1094            midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[iKey];
1095          pKey->KeyPressed = false; // the MIDI key was now released          pKey->KeyPressed = false; // the MIDI key was now released
1096    
1097          // release voices on this key if needed          // move event to the key's own event list
1098          if (pKey->Active && ShouldReleaseVoice(pEngineChannel, itNoteOffEvent->Param.Note.Key)) {          RTList<Event>::Iterator itNoteOffEventOnKeyList = itNoteOffEvent.moveToEndOf(pKey->pEvents);
1099              itNoteOffEvent->Type = Event::type_release; // transform event type  
1100            bool bShouldRelease = pKey->Active && ShouldReleaseVoice(pEngineChannel, itNoteOffEventOnKeyList->Param.Note.Key);
1101    
1102              // move event to the key's own event list          // in case Solo Mode is enabled, kill all voices on this key and respawn a voice on the highest pressed key (if any)
1103              RTList<Event>::Iterator itNoteOffEventOnKeyList = itNoteOffEvent.moveToEndOf(pKey->pEvents);          if (pEngineChannel->SoloMode && pEngineChannel->pInstrument) { //TODO: this feels like too much code just for handling solo mode :P
1104                bool bOtherKeysPressed = false;
1105                if (iKey == pEngineChannel->SoloKey) {
1106                    pEngineChannel->SoloKey = -1;
1107                    // if there's still a key pressed down, respawn a voice (group) on the highest key
1108                    for (int i = 127; i > 0; i--) {
1109                        midi_key_info_t* pOtherKey = &pEngineChannel->pMIDIKeyInfo[i];
1110                        if (pOtherKey->KeyPressed) {
1111                            bOtherKeysPressed = true;
1112                            // make the other key the new 'currently active solo key'
1113                            pEngineChannel->SoloKey = i;
1114                            // get final portamento position of currently active voice
1115                            if (pEngineChannel->PortamentoMode) {
1116                                RTList<Voice>::Iterator itVoice = pKey->pActiveVoices->first();
1117                                if (itVoice) itVoice->UpdatePortamentoPos(itNoteOffEventOnKeyList);
1118                            }
1119                            // create a pseudo note on event
1120                            RTList<Event>::Iterator itPseudoNoteOnEvent = pOtherKey->pEvents->allocAppend();
1121                            if (itPseudoNoteOnEvent) {
1122                                // copy event
1123                                *itPseudoNoteOnEvent = *itNoteOffEventOnKeyList;
1124                                // transform event to a note on event
1125                                itPseudoNoteOnEvent->Type                = Event::type_note_on;
1126                                itPseudoNoteOnEvent->Param.Note.Key      = i;
1127                                itPseudoNoteOnEvent->Param.Note.Velocity = pOtherKey->Velocity;
1128                                // allocate and trigger new voice(s) for the other key
1129                                {
1130                                    // first, get total amount of required voices (dependant on amount of layers)
1131                                    ::gig::Region* pRegion = pEngineChannel->pInstrument->GetRegion(i);
1132                                    if (pRegion) {
1133                                        int voicesRequired = pRegion->Layers;
1134                                        // now launch the required amount of voices
1135                                        for (int iLayer = 0; iLayer < voicesRequired; iLayer++)
1136                                            LaunchVoice(pEngineChannel, itPseudoNoteOnEvent, iLayer, false, true, false);
1137                                    }
1138                                }
1139                                // if neither a voice was spawned or postponed then remove note on event from key again
1140                                if (!pOtherKey->Active && !pOtherKey->VoiceTheftsQueued)
1141                                    pOtherKey->pEvents->free(itPseudoNoteOnEvent);
1142    
1143                            } else dmsg(1,("Could not respawn voice, no free event left\n"));
1144                            break; // done
1145                        }
1146                    }
1147                }
1148                if (bOtherKeysPressed) {
1149                    if (pKey->Active) { // kill all voices on this key
1150                        bShouldRelease = false; // no need to release, as we kill it here
1151                        RTList<Voice>::Iterator itVoiceToBeKilled = pKey->pActiveVoices->first();
1152                        RTList<Voice>::Iterator end               = pKey->pActiveVoices->end();
1153                        for (; itVoiceToBeKilled != end; ++itVoiceToBeKilled) {
1154                            if (itVoiceToBeKilled->Type != Voice::type_release_trigger)
1155                                itVoiceToBeKilled->Kill(itNoteOffEventOnKeyList);
1156                        }
1157                    }
1158                } else pEngineChannel->PortamentoPos = -1.0f;
1159            }
1160    
1161            // if no solo mode (the usual case) or if solo mode and no other key pressed, then release voices on this key if needed
1162            if (bShouldRelease) {
1163                itNoteOffEventOnKeyList->Type = Event::type_release; // transform event type
1164    
1165              // spawn release triggered voice(s) if needed              // spawn release triggered voice(s) if needed
1166              if (pKey->ReleaseTrigger) {              if (pKey->ReleaseTrigger && pEngineChannel->pInstrument) {
1167                  // first, get total amount of required voices (dependant on amount of layers)                  // first, get total amount of required voices (dependant on amount of layers)
1168                  ::gig::Region* pRegion = pEngineChannel->pInstrument->GetRegion(itNoteOffEventOnKeyList->Param.Note.Key);                  ::gig::Region* pRegion = pEngineChannel->pInstrument->GetRegion(itNoteOffEventOnKeyList->Param.Note.Key);
1169                  if (pRegion) {                  if (pRegion) {
# Line 687  namespace LinuxSampler { namespace gig { Line 1178  namespace LinuxSampler { namespace gig {
1178                  }                  }
1179                  pKey->ReleaseTrigger = false;                  pKey->ReleaseTrigger = false;
1180              }              }
   
             // 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);  
1181          }          }
1182    
1183            // if neither a voice was spawned or postponed on this key then remove note off event from key again
1184            if (!pKey->Active && !pKey->VoiceTheftsQueued)
1185                pKey->pEvents->free(itNoteOffEventOnKeyList);
1186      }      }
1187    
1188      /**      /**
# Line 775  namespace LinuxSampler { namespace gig { Line 1266  namespace LinuxSampler { namespace gig {
1266                      DimValues[i] = itNoteOnEvent->Param.Note.Velocity;                      DimValues[i] = itNoteOnEvent->Param.Note.Velocity;
1267                      break;                      break;
1268                  case ::gig::dimension_channelaftertouch:                  case ::gig::dimension_channelaftertouch:
1269                      DimValues[i] = 0; //TODO: we currently ignore this dimension                      DimValues[i] = pEngineChannel->ControllerTable[128];
1270                      break;                      break;
1271                  case ::gig::dimension_releasetrigger:                  case ::gig::dimension_releasetrigger:
1272                      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;
1273                      DimValues[i] = (uint) ReleaseTriggerVoice;                      DimValues[i] = (uint) ReleaseTriggerVoice;
1274                      break;                      break;
1275                  case ::gig::dimension_keyboard:                  case ::gig::dimension_keyboard:
1276                      DimValues[i] = (uint) pEngineChannel->CurrentKeyDimension;                      DimValues[i] = (uint) (pEngineChannel->CurrentKeyDimension * pRegion->pDimensionDefinitions[i].zones);
1277                      break;                      break;
1278                  case ::gig::dimension_roundrobin:                  case ::gig::dimension_roundrobin:
1279                      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 1358  namespace LinuxSampler { namespace gig {
1358                      std::cerr << "gig::Engine::LaunchVoice() Error: Unknown dimension\n" << std::flush;                      std::cerr << "gig::Engine::LaunchVoice() Error: Unknown dimension\n" << std::flush;
1359              }              }
1360          }          }
1361    
1362            // return if this is a release triggered voice and there is no
1363            // releasetrigger dimension (could happen if an instrument
1364            // change has occured between note on and off)
1365            if (ReleaseTriggerVoice && VoiceType != Voice::type_release_trigger) return Pool<Voice>::Iterator();
1366    
1367          ::gig::DimensionRegion* pDimRgn = pRegion->GetDimensionRegionByValue(DimValues);          ::gig::DimensionRegion* pDimRgn = pRegion->GetDimensionRegionByValue(DimValues);
1368    
1369          // no need to continue if sample is silent          // no need to continue if sample is silent
# Line 1096  namespace LinuxSampler { namespace gig { Line 1593  namespace LinuxSampler { namespace gig {
1593    
1594              uint keygroup = itVoice->KeyGroup;              uint keygroup = itVoice->KeyGroup;
1595    
1596                // if the sample and dimension region belong to an
1597                // instrument that is unloaded, tell the disk thread to
1598                // release them
1599                if (itVoice->Orphan) {
1600                    pDiskThread->OrderDeletionOfDimreg(itVoice->pDimRgn);
1601                }
1602    
1603              // free the voice object              // free the voice object
1604              pVoicePool->free(itVoice);              pVoicePool->free(itVoice);
1605    
# Line 1137  namespace LinuxSampler { namespace gig { Line 1641  namespace LinuxSampler { namespace gig {
1641      void Engine::ProcessControlChange(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itControlChangeEvent) {      void Engine::ProcessControlChange(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itControlChangeEvent) {
1642          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));
1643    
1644            // handle the "control triggered" MIDI rule: a control change
1645            // event can trigger a new note on or note off event
1646            if (pEngineChannel->pInstrument) {
1647    
1648                ::gig::MidiRule* rule;
1649                for (int i = 0 ; (rule = pEngineChannel->pInstrument->GetMidiRule(i)) ; i++) {
1650    
1651                    if (::gig::MidiRuleCtrlTrigger* ctrlTrigger =
1652                        dynamic_cast< ::gig::MidiRuleCtrlTrigger*>(rule)) {
1653                        if (itControlChangeEvent->Param.CC.Controller ==
1654                            ctrlTrigger->ControllerNumber) {
1655    
1656                            uint8_t oldCCValue = pEngineChannel->ControllerTable[
1657                                itControlChangeEvent->Param.CC.Controller];
1658                            uint8_t newCCValue = itControlChangeEvent->Param.CC.Value;
1659    
1660                            for (int i = 0 ; i < ctrlTrigger->Triggers ; i++) {
1661                                ::gig::MidiRuleCtrlTrigger::trigger_t* pTrigger =
1662                                      &ctrlTrigger->pTriggers[i];
1663    
1664                                // check if the controller has passed the
1665                                // trigger point in the right direction
1666                                if ((pTrigger->Descending &&
1667                                     oldCCValue > pTrigger->TriggerPoint &&
1668                                     newCCValue <= pTrigger->TriggerPoint) ||
1669                                    (!pTrigger->Descending &&
1670                                     oldCCValue < pTrigger->TriggerPoint &&
1671                                     newCCValue >= pTrigger->TriggerPoint)) {
1672    
1673                                    RTList<Event>::Iterator itNewEvent = pGlobalEvents->allocAppend();
1674                                    if (itNewEvent) {
1675                                        *itNewEvent = *itControlChangeEvent;
1676                                        itNewEvent->Param.Note.Key = pTrigger->Key;
1677    
1678                                        if (pTrigger->NoteOff || pTrigger->Velocity == 0) {
1679                                            itNewEvent->Type = Event::type_note_off;
1680                                            itNewEvent->Param.Note.Velocity = 100;
1681    
1682                                            ProcessNoteOff(pEngineChannel, itNewEvent);
1683                                        } else {
1684                                            itNewEvent->Type = Event::type_note_on;
1685                                            //TODO: if Velocity is 255, the triggered velocity should
1686                                            // depend on how fast the controller is moving
1687                                            itNewEvent->Param.Note.Velocity =
1688                                                pTrigger->Velocity == 255 ? 100 :
1689                                                pTrigger->Velocity;
1690    
1691                                            ProcessNoteOn(pEngineChannel, itNewEvent);
1692                                        }
1693                                    }
1694                                    else dmsg(1,("Event pool emtpy!\n"));
1695                                }
1696                            }
1697                        }
1698                    }
1699                }
1700            }
1701    
1702          // update controller value in the engine channel's controller table          // update controller value in the engine channel's controller table
1703          pEngineChannel->ControllerTable[itControlChangeEvent->Param.CC.Controller] = itControlChangeEvent->Param.CC.Value;          pEngineChannel->ControllerTable[itControlChangeEvent->Param.CC.Controller] = itControlChangeEvent->Param.CC.Value;
1704    
1705            // handle hard coded MIDI controllers
1706          switch (itControlChangeEvent->Param.CC.Controller) {          switch (itControlChangeEvent->Param.CC.Controller) {
1707                case 5: { // portamento time
1708                    pEngineChannel->PortamentoTime = (float) itControlChangeEvent->Param.CC.Value / 127.0f * (float) CONFIG_PORTAMENTO_TIME_MAX + (float) CONFIG_PORTAMENTO_TIME_MIN;
1709                    break;
1710                }
1711                case 6: { // data entry (currently only used for RPN controllers)
1712                    if (pEngineChannel->GetMidiRpnController() == 2) { // coarse tuning in half tones
1713                        int transpose = (int) itControlChangeEvent->Param.CC.Value - 64;
1714                        // limit to +- two octaves for now
1715                        transpose = RTMath::Min(transpose,  24);
1716                        transpose = RTMath::Max(transpose, -24);
1717                        pEngineChannel->GlobalTranspose = transpose;
1718                        // workaround, so we won't have hanging notes
1719                        ReleaseAllVoices(pEngineChannel, itControlChangeEvent);
1720                    }
1721                    // to avoid other MIDI CC #6 messages to be misenterpreted as RPN controller data
1722                    pEngineChannel->ResetMidiRpnController();
1723                    break;
1724                }
1725              case 7: { // volume              case 7: { // volume
1726                  //TODO: not sample accurate yet                  //TODO: not sample accurate yet
1727                  pEngineChannel->GlobalVolume = (float) itControlChangeEvent->Param.CC.Value / 127.0f *  CONFIG_GLOBAL_ATTENUATION;                  pEngineChannel->MidiVolume = VolumeCurve[itControlChangeEvent->Param.CC.Value];
1728                  pEngineChannel->bStatusChanged = true; // engine channel status has changed, so set notify flag                  pEngineChannel->bStatusChanged = true; // engine channel status has changed, so set notify flag
1729                  break;                  break;
1730              }              }
1731              case 10: { // panpot              case 10: { // panpot
1732                  //TODO: not sample accurate yet                  //TODO: not sample accurate yet
1733                  const int pan = (int) itControlChangeEvent->Param.CC.Value - 64;                  pEngineChannel->GlobalPanLeft  = PanCurve[128 - itControlChangeEvent->Param.CC.Value];
1734                  pEngineChannel->GlobalPanLeft  = 1.0f - float(RTMath::Max(pan, 0)) /  63.0f;                  pEngineChannel->GlobalPanRight = PanCurve[itControlChangeEvent->Param.CC.Value];
1735                  pEngineChannel->GlobalPanRight = 1.0f - float(RTMath::Min(pan, 0)) / -64.0f;                  pEngineChannel->iLastPanRequest = itControlChangeEvent->Param.CC.Value;
1736                  break;                  break;
1737              }              }
1738              case 64: { // sustain              case 64: { // sustain
# Line 1201  namespace LinuxSampler { namespace gig { Line 1782  namespace LinuxSampler { namespace gig {
1782                  }                  }
1783                  break;                  break;
1784              }              }
1785                case 65: { // portamento on / off
1786                    const bool bPortamento = itControlChangeEvent->Param.CC.Value >= 64;
1787                    if (bPortamento != pEngineChannel->PortamentoMode)
1788                        KillAllVoices(pEngineChannel, itControlChangeEvent);
1789                    pEngineChannel->PortamentoMode = bPortamento;
1790                    break;
1791                }
1792              case 66: { // sostenuto              case 66: { // sostenuto
1793                  if (itControlChangeEvent->Param.CC.Value >= 64 && !pEngineChannel->SostenutoPedal) {                  if (itControlChangeEvent->Param.CC.Value >= 64 && !pEngineChannel->SostenutoPedal) {
1794                      dmsg(4,("SOSTENUTO (CENTER) PEDAL DOWN\n"));                      dmsg(4,("SOSTENUTO (CENTER) PEDAL DOWN\n"));
# Line 1241  namespace LinuxSampler { namespace gig { Line 1829  namespace LinuxSampler { namespace gig {
1829                  }                  }
1830                  break;                  break;
1831              }              }
1832                case 100: { // RPN controller LSB
1833                    pEngineChannel->SetMidiRpnControllerLsb(itControlChangeEvent->Param.CC.Value);
1834                    break;
1835                }
1836                case 101: { // RPN controller MSB
1837                    pEngineChannel->SetMidiRpnControllerMsb(itControlChangeEvent->Param.CC.Value);
1838                    break;
1839                }
1840    
1841    
1842              // Channel Mode Messages              // Channel Mode Messages
# Line 1254  namespace LinuxSampler { namespace gig { Line 1850  namespace LinuxSampler { namespace gig {
1850                  break;                  break;
1851              }              }
1852              case 123: { // all notes off              case 123: { // all notes off
1853                    #if CONFIG_PROCESS_ALL_NOTES_OFF
1854                  ReleaseAllVoices(pEngineChannel, itControlChangeEvent);                  ReleaseAllVoices(pEngineChannel, itControlChangeEvent);
1855                    #endif // CONFIG_PROCESS_ALL_NOTES_OFF
1856                    break;
1857                }
1858                case 126: { // mono mode on
1859                    if (!pEngineChannel->SoloMode)
1860                        KillAllVoices(pEngineChannel, itControlChangeEvent);
1861                    pEngineChannel->SoloMode = true;
1862                  break;                  break;
1863              }              }
1864                case 127: { // poly mode on
1865                    if (pEngineChannel->SoloMode)
1866                        KillAllVoices(pEngineChannel, itControlChangeEvent);
1867                    pEngineChannel->SoloMode = false;
1868                    break;
1869                }
1870            }
1871    
1872            // handle FX send controllers
1873            if (!pEngineChannel->fxSends.empty()) {
1874                for (int iFxSend = 0; iFxSend < pEngineChannel->GetFxSendCount(); iFxSend++) {
1875                    FxSend* pFxSend = pEngineChannel->GetFxSend(iFxSend);
1876                    if (pFxSend->MidiController() == itControlChangeEvent->Param.CC.Controller) {
1877                        pFxSend->SetLevel(itControlChangeEvent->Param.CC.Value);
1878                        pFxSend->SetInfoChanged(true);
1879                    }
1880                }
1881          }          }
1882      }      }
1883    
# Line 1266  namespace LinuxSampler { namespace gig { Line 1887  namespace LinuxSampler { namespace gig {
1887       *  @param itSysexEvent - sysex data size and time stamp of the sysex event       *  @param itSysexEvent - sysex data size and time stamp of the sysex event
1888       */       */
1889      void Engine::ProcessSysex(Pool<Event>::Iterator& itSysexEvent) {      void Engine::ProcessSysex(Pool<Event>::Iterator& itSysexEvent) {
1890          RingBuffer<uint8_t>::NonVolatileReader reader = pSysexBuffer->get_non_volatile_reader();          RingBuffer<uint8_t,false>::NonVolatileReader reader = pSysexBuffer->get_non_volatile_reader();
1891    
1892          uint8_t exclusive_status, id;          uint8_t exclusive_status, id;
1893          if (!reader.pop(&exclusive_status)) goto free_sysex_data;          if (!reader.pop(&exclusive_status)) goto free_sysex_data;
# Line 1274  namespace LinuxSampler { namespace gig { Line 1895  namespace LinuxSampler { namespace gig {
1895          if (exclusive_status != 0xF0)       goto free_sysex_data;          if (exclusive_status != 0xF0)       goto free_sysex_data;
1896    
1897          switch (id) {          switch (id) {
1898                case 0x7f: { // (Realtime) Universal Sysex (GM Standard)
1899                    uint8_t sysex_channel, sub_id1, sub_id2, val_msb, val_lsb;;
1900                    if (!reader.pop(&sysex_channel)) goto free_sysex_data;
1901                    if (!reader.pop(&sub_id1)) goto free_sysex_data;
1902                    if (!reader.pop(&sub_id2)) goto free_sysex_data;
1903                    if (!reader.pop(&val_lsb)) goto free_sysex_data;
1904                    if (!reader.pop(&val_msb)) goto free_sysex_data;
1905                    //TODO: for now we simply ignore the sysex channel, seldom used anyway
1906                    switch (sub_id1) {
1907                        case 0x04: // Device Control
1908                            switch (sub_id2) {
1909                                case 0x01: { // Master Volume
1910                                    const double volume =
1911                                        double((uint(val_msb)<<7) | uint(val_lsb)) / 16383.0;
1912                                    #if CONFIG_MASTER_VOLUME_SYSEX_BY_PORT
1913                                    // apply volume to all sampler channels that
1914                                    // are connected to the same MIDI input port
1915                                    // this sysex message arrived on
1916                                    for (int i = 0; i < engineChannels.size(); ++i) {
1917                                        EngineChannel* pEngineChannel = engineChannels[i];
1918                                        if (pEngineChannel->GetMidiInputPort() ==
1919                                            itSysexEvent->pMidiInputPort)
1920                                        {
1921                                            pEngineChannel->Volume(volume);
1922                                        }
1923                                    }
1924                                    #else
1925                                    // apply volume globally to the whole sampler
1926                                    GLOBAL_VOLUME = volume;
1927                                    #endif // CONFIG_MASTER_VOLUME_SYSEX_BY_PORT
1928                                    break;
1929                                }
1930                            }
1931                            break;
1932                    }
1933                    break;
1934                }
1935              case 0x41: { // Roland              case 0x41: { // Roland
1936                  dmsg(3,("Roland Sysex\n"));                  dmsg(3,("Roland Sysex\n"));
1937                  uint8_t device_id, model_id, cmd_id;                  uint8_t device_id, model_id, cmd_id;
# Line 1285  namespace LinuxSampler { namespace gig { Line 1943  namespace LinuxSampler { namespace gig {
1943    
1944                  // command address                  // command address
1945                  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)
1946                  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
1947                  if (reader.read(&addr[0], 3) != 3) goto free_sysex_data;                  if (reader.read(&addr[0], 3) != 3) goto free_sysex_data;
1948                  if (addr[0] == 0x40 && addr[1] == 0x00) { // System Parameters                  if (addr[0] == 0x40 && addr[1] == 0x00) { // System Parameters
1949                      dmsg(3,("\tSystem Parameter\n"));                      dmsg(3,("\tSystem Parameter\n"));
# Line 1310  namespace LinuxSampler { namespace gig { Line 1968  namespace LinuxSampler { namespace gig {
1968                              dmsg(3,("\t\t\tNew scale applied.\n"));                              dmsg(3,("\t\t\tNew scale applied.\n"));
1969                              break;                              break;
1970                          }                          }
1971                            case 0x15: { // chromatic / drumkit mode
1972                                dmsg(3,("\t\tMIDI Instrument Map Switch\n"));
1973                                uint8_t part = addr[1] & 0x0f;
1974                                uint8_t map;
1975                                if (!reader.pop(&map)) goto free_sysex_data;
1976                                for (int i = 0; i < engineChannels.size(); ++i) {
1977                                    EngineChannel* pEngineChannel = engineChannels[i];
1978                                    if (
1979                                        (pEngineChannel->midiChannel == part ||
1980                                         pEngineChannel->midiChannel == midi_chan_all) &&
1981                                         pEngineChannel->GetMidiInputPort() == itSysexEvent->pMidiInputPort
1982                                    ) {
1983                                        try {
1984                                            pEngineChannel->SetMidiInstrumentMap(map);
1985                                        } catch (Exception e) {
1986                                            dmsg(2,("\t\t\tCould not apply MIDI instrument map %d to part %d: %s\n", map, part, e.Message().c_str()));
1987                                            goto free_sysex_data;
1988                                        } catch (...) {
1989                                            dmsg(2,("\t\t\tCould not apply MIDI instrument map %d to part %d (unknown exception)\n", map, part));
1990                                            goto free_sysex_data;
1991                                        }
1992                                    }
1993                                }
1994                                dmsg(3,("\t\t\tApplied MIDI instrument map %d to part %d.\n", map, part));
1995                                break;
1996                            }
1997                      }                      }
1998                  }                  }
1999                  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 1332  namespace LinuxSampler { namespace gig { Line 2016  namespace LinuxSampler { namespace gig {
2016       *                     question       *                     question
2017       * @param DataSize   - size of the GS message data (in bytes)       * @param DataSize   - size of the GS message data (in bytes)
2018       */       */
2019      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) {
2020          RingBuffer<uint8_t>::NonVolatileReader reader = AddrReader;          RingBuffer<uint8_t,false>::NonVolatileReader reader = AddrReader;
2021          uint bytes = 3 /*addr*/ + DataSize;          uint bytes = 3 /*addr*/ + DataSize;
2022          uint8_t addr_and_data[bytes];          uint8_t addr_and_data[bytes];
2023          reader.read(&addr_and_data[0], bytes);          reader.read(&addr_and_data[0], bytes);
# Line 1448  namespace LinuxSampler { namespace gig { Line 2132  namespace LinuxSampler { namespace gig {
2132      }      }
2133    
2134      String Engine::Description() {      String Engine::Description() {
2135          return "Gigasampler Engine";          return "Gigasampler Format Engine";
2136      }      }
2137    
2138      String Engine::Version() {      String Engine::Version() {
2139          String s = "$Revision: 1.56 $";          String s = "$Revision: 1.97 $";
2140          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
2141      }      }
2142    
2143        InstrumentManager* Engine::GetInstrumentManager() {
2144            return &instruments;
2145        }
2146    
2147        // static constant initializers
2148        const Engine::FloatTable Engine::VolumeCurve(InitVolumeCurve());
2149        const Engine::FloatTable Engine::PanCurve(InitPanCurve());
2150        const Engine::FloatTable Engine::CrossfadeCurve(InitCrossfadeCurve());
2151    
2152        float* Engine::InitVolumeCurve() {
2153            // line-segment approximation
2154            const float segments[] = {
2155                0, 0, 2, 0.0046, 16, 0.016, 31, 0.051, 45, 0.115, 54.5, 0.2,
2156                64.5, 0.39, 74, 0.74, 92, 1.03, 114, 1.94, 119.2, 2.2, 127, 2.2
2157            };
2158            return InitCurve(segments);
2159        }
2160    
2161        float* Engine::InitPanCurve() {
2162            // line-segment approximation
2163            const float segments[] = {
2164                0, 0, 1, 0,
2165                2, 0.05, 31.5, 0.7, 51, 0.851, 74.5, 1.12,
2166                127, 1.41, 128, 1.41
2167            };
2168            return InitCurve(segments, 129);
2169        }
2170    
2171        float* Engine::InitCrossfadeCurve() {
2172            // line-segment approximation
2173            const float segments[] = {
2174                0, 0, 1, 0.03, 10, 0.1, 51, 0.58, 127, 1
2175            };
2176            return InitCurve(segments);
2177        }
2178    
2179        float* Engine::InitCurve(const float* segments, int size) {
2180            float* y = new float[size];
2181            for (int x = 0 ; x < size ; x++) {
2182                if (x > segments[2]) segments += 2;
2183                y[x] = segments[1] + (x - segments[0]) *
2184                    (segments[3] - segments[1]) / (segments[2] - segments[0]);
2185            }
2186            return y;
2187        }
2188    
2189  }} // namespace LinuxSampler::gig  }} // namespace LinuxSampler::gig

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

  ViewVC Help
Powered by ViewVC