/[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 831 by persson, Sat Jan 28 16:55:30 2006 UTC revision 1933 by persson, Thu Jul 9 17:37:41 2009 UTC
# Line 2  Line 2 
2   *                                                                         *   *                                                                         *
3   *   LinuxSampler - modular, streaming capable sampler                     *   *   LinuxSampler - modular, streaming capable sampler                     *
4   *                                                                         *   *                                                                         *
5   *   Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck   *   *   Copyright (C) 2003,2004 by Benno Senoner and Christian Schoenebeck    *
6   *   Copyright (C) 2005, 2006 Christian Schoenebeck                        *   *   Copyright (C) 2005-2009 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>(GLOBAL_MAX_VOICES);
111            pDimRegionPool[0]  = new Pool< ::gig::DimensionRegion*>(GLOBAL_MAX_VOICES);
112            pDimRegionPool[1]  = new Pool< ::gig::DimensionRegion*>(GLOBAL_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            iMaxDiskStreams    = GLOBAL_MAX_STREAMS;
116            FrameTime          = 0;
117    
118          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()) {
119              iterVoice->SetEngine(this);              iterVoice->SetEngine(this);
120          }          }
# Line 110  namespace LinuxSampler { namespace gig { Line 122  namespace LinuxSampler { namespace gig {
122    
123          ResetInternal();          ResetInternal();
124          ResetScaleTuning();          ResetScaleTuning();
125            ResetSuspendedRegions();
126      }      }
127    
128      /**      /**
129       * Destructor       * Destructor
130       */       */
131      Engine::~Engine() {      Engine::~Engine() {
132            MidiInputPort::RemoveSysexListener(this);
133          if (pDiskThread) {          if (pDiskThread) {
134              dmsg(1,("Stopping disk thread..."));              dmsg(1,("Stopping disk thread..."));
135              pDiskThread->StopThread();              pDiskThread->StopThread();
# Line 131  namespace LinuxSampler { namespace gig { Line 145  namespace LinuxSampler { namespace gig {
145          if (pEventGenerator) delete pEventGenerator;          if (pEventGenerator) delete pEventGenerator;
146          if (pVoiceStealingQueue) delete pVoiceStealingQueue;          if (pVoiceStealingQueue) delete pVoiceStealingQueue;
147          if (pSysexBuffer) delete pSysexBuffer;          if (pSysexBuffer) delete pSysexBuffer;
148          EngineFactory::Destroy(this);          if (pGlobalEvents) delete pGlobalEvents;
149            if (pDimRegionPool[0]) delete pDimRegionPool[0];
150            if (pDimRegionPool[1]) delete pDimRegionPool[1];
151            ResetSuspendedRegions();
152            Unregister();
153      }      }
154    
155      void Engine::Enable() {      void Engine::Enable() {
156          dmsg(3,("gig::Engine: enabling\n"));          dmsg(3,("gig::Engine: enabling\n"));
157          EngineDisabled.PushAndUnlock(false, 2); // set condition object 'EngineDisabled' to false (wait max. 2s)          EngineDisabled.PushAndUnlock(false, 2, 0, true); // set condition object 'EngineDisabled' to false (wait max. 2s)
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                        // free the voice to the voice pool and update key info
217                        FreeVoice(pEngineChannel, itVoice);
218                    }
219                }
220            }
221            // wait until all streams were actually deleted by the disk thread
222            while (iPendingStreamDeletions) {
223                while (
224                    iPendingStreamDeletions &&
225                    pDiskThread->AskForDeletedStream() != Stream::INVALID_HANDLE
226                ) iPendingStreamDeletions--;
227                if (!iPendingStreamDeletions) break;
228                usleep(10000); // sleep for 10ms
229            }
230            dmsg(2,("gig::Engine: Everything suspended.\n"));
231        }
232    
233        /**
234         * At the moment same as calling @c Enable() directly, but this might
235         * change in future, so better call this method as counterpart to
236         * @c SuspendAll() instead of @c Enable() !
237         */
238        void Engine::ResumeAll() {
239            Enable();
240        }
241    
242        /**
243         * Order the engine to stop rendering audio for the given region.
244         * Additionally this method will block until all voices and their disk
245         * streams associated with that region are actually killed / deleted, so
246         * one can i.e. safely modify the region with an instrument editor after
247         * returning from this method.
248         *
249         * @param pRegion - region the engine shall stop using
250         */
251        void Engine::Suspend(::gig::Region* pRegion) {
252            dmsg(2,("gig::Engine: Suspending Region %x ...\n",pRegion));
253            SuspendedRegionsMutex.Lock();
254            SuspensionChangeOngoing.Set(true);
255            pPendingRegionSuspension = pRegion;
256            SuspensionChangeOngoing.WaitAndUnlockIf(true);
257            SuspendedRegionsMutex.Unlock();
258            dmsg(2,("gig::Engine: Region %x suspended.",pRegion));
259        }
260    
261        /**
262         * Orders the engine to resume playing back the given region, previously
263         * suspended with @c Suspend() .
264         *
265         * @param pRegion - region the engine shall be allowed to use again
266         */
267        void Engine::Resume(::gig::Region* pRegion) {
268            dmsg(2,("gig::Engine: Resuming Region %x ...\n",pRegion));
269            SuspendedRegionsMutex.Lock();
270            SuspensionChangeOngoing.Set(true);
271            pPendingRegionResumption = pRegion;
272            SuspensionChangeOngoing.WaitAndUnlockIf(true);
273            SuspendedRegionsMutex.Unlock();
274            dmsg(2,("gig::Engine: Region %x resumed.\n",pRegion));
275        }
276    
277        /**
278       *  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
279       *  control and status variables.       *  control and status variables.
280       */       */
# Line 165  namespace LinuxSampler { namespace gig { Line 287  namespace LinuxSampler { namespace gig {
287    
288      /**      /**
289       *  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
290       *  control and status variables. This method is not thread safe!       *  control and status variables. This method is protected by a mutex.
291       */       */
292      void Engine::ResetInternal() {      void Engine::ResetInternal() {
293          ActiveVoiceCount    = 0;          ResetInternalMutex.Lock();
294    
295            // make sure that the engine does not get any sysex messages
296            // while it's reseting
297            bool sysexDisabled = MidiInputPort::RemoveSysexListener(this);
298            SetVoiceCount(0);
299          ActiveVoiceCountMax = 0;          ActiveVoiceCountMax = 0;
300    
301          // reset voice stealing parameters          // reset voice stealing parameters
# Line 191  namespace LinuxSampler { namespace gig { Line 318  namespace LinuxSampler { namespace gig {
318          // delete all input events          // delete all input events
319          pEventQueue->init();          pEventQueue->init();
320          pSysexBuffer->init();          pSysexBuffer->init();
321            if (sysexDisabled) MidiInputPort::AddSysexListener(this);
322            ResetInternalMutex.Unlock();
323      }      }
324    
325      /**      /**
# Line 200  namespace LinuxSampler { namespace gig { Line 329  namespace LinuxSampler { namespace gig {
329          memset(&ScaleTuning[0], 0x00, 12);          memset(&ScaleTuning[0], 0x00, 12);
330      }      }
331    
332        void Engine::ResetSuspendedRegions() {
333            SuspendedRegions.clear();
334            iPendingStreamDeletions = 0;
335            pPendingRegionSuspension = pPendingRegionResumption = NULL;
336            SuspensionChangeOngoing.Set(false);
337        }
338    
339      /**      /**
340       * Connect this engine instance with the given audio output device.       * Connect this engine instance with the given audio output device.
341       * This method will be called when an Engine instance is created.       * This method will be called when an Engine instance is created.
# Line 210  namespace LinuxSampler { namespace gig { Line 346  namespace LinuxSampler { namespace gig {
346       * @param pAudioOut - audio output device to connect to       * @param pAudioOut - audio output device to connect to
347       */       */
348      void Engine::Connect(AudioOutputDevice* pAudioOut) {      void Engine::Connect(AudioOutputDevice* pAudioOut) {
349            // caution: don't ignore if connecting to the same device here,
350            // because otherwise SetMaxDiskStreams() implementation won't work anymore!
351    
352          pAudioOutputDevice = pAudioOut;          pAudioOutputDevice = pAudioOut;
353    
354          ResetInternal();          ResetInternal();
# Line 220  namespace LinuxSampler { namespace gig { Line 359  namespace LinuxSampler { namespace gig {
359          }          }
360          catch (AudioOutputException e) {          catch (AudioOutputException e) {
361              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();
362              throw LinuxSamplerException(msg);              throw Exception(msg);
363          }          }
364    
365          this->MaxSamplesPerCycle = pAudioOutputDevice->MaxSamplesPerCycle();          this->MaxSamplesPerCycle = pAudioOutputDevice->MaxSamplesPerCycle();
366          this->SampleRate         = pAudioOutputDevice->SampleRate();          this->SampleRate         = pAudioOutputDevice->SampleRate();
367    
368          // FIXME: audio drivers with varying fragment sizes might be a problem here          MinFadeOutSamples = int(double(SampleRate) * CONFIG_EG_MIN_RELEASE_TIME) - 1;
369          MaxFadeOutPos = MaxSamplesPerCycle - int(double(SampleRate) * CONFIG_EG_MIN_RELEASE_TIME) - 1;          if (MaxSamplesPerCycle < MinFadeOutSamples) {
         if (MaxFadeOutPos < 0) {  
370              std::cerr << "gig::Engine: WARNING, CONFIG_EG_MIN_RELEASE_TIME "              std::cerr << "gig::Engine: WARNING, CONFIG_EG_MIN_RELEASE_TIME "
371                        << "too big for current audio fragment size & sampling rate! "                        << "too big for current audio fragment size & sampling rate! "
372                        << "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;
373              // force volume ramp downs at the beginning of each fragment              // force volume ramp downs at the beginning of each fragment
374              MaxFadeOutPos = 0;              MinFadeOutSamples = MaxSamplesPerCycle;
375              // lower minimum release time              // lower minimum release time
376              const float minReleaseTime = (float) MaxSamplesPerCycle / (float) SampleRate;              const float minReleaseTime = (float) MaxSamplesPerCycle / (float) SampleRate;
377              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 387  namespace LinuxSampler { namespace gig {
387              delete this->pDiskThread;              delete this->pDiskThread;
388              dmsg(1,("OK\n"));              dmsg(1,("OK\n"));
389          }          }
390          this->pDiskThread = new DiskThread(((pAudioOut->MaxSamplesPerCycle() << CONFIG_MAX_PITCH) << 1) + 6); //FIXME: assuming stereo          this->pDiskThread =
391                new DiskThread(
392                    iMaxDiskStreams,
393                    ((pAudioOut->MaxSamplesPerCycle() << CONFIG_MAX_PITCH) << 1) + 6, //FIXME: assuming stereo
394                    &instruments
395                );
396    
397          if (!pDiskThread) {          if (!pDiskThread) {
398              dmsg(0,("gig::Engine  new diskthread = NULL\n"));              dmsg(0,("gig::Engine  new diskthread = NULL\n"));
399              exit(EXIT_FAILURE);              exit(EXIT_FAILURE);
# Line 275  namespace LinuxSampler { namespace gig { Line 419  namespace LinuxSampler { namespace gig {
419                  exit(EXIT_FAILURE);                  exit(EXIT_FAILURE);
420              }              }
421          }          }
422            pVoicePool->clear();
423        }
424    
425        /**
426         * Called by the engine's (audio) thread once per cycle to process requests
427         * from the outer world to suspend or resume a given @c gig::Region .
428         */
429        void Engine::ProcessSuspensionsChanges() {
430            // process request for suspending one region
431            if (pPendingRegionSuspension) {
432                // kill all voices on all engine channels that use this region
433                for (int iChannel = 0; iChannel < engineChannels.size(); iChannel++) {
434                    EngineChannel* pEngineChannel = engineChannels[iChannel];
435                    RTList<uint>::Iterator iuiKey = pEngineChannel->pActiveKeys->first();
436                    RTList<uint>::Iterator end    = pEngineChannel->pActiveKeys->end();
437                    for (; iuiKey != end; ++iuiKey) { // iterate through all active keys
438                        midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[*iuiKey];
439                        RTList<Voice>::Iterator itVoice = pKey->pActiveVoices->first();
440                        // if current key is not associated with this region, skip this key
441                        if (itVoice->pDimRgn->GetParent() != pPendingRegionSuspension) continue;
442                        RTList<Voice>::Iterator itVoicesEnd = pKey->pActiveVoices->end();
443                        for (; itVoice != itVoicesEnd; ++itVoice) { // iterate through all voices on this key
444                            // request a notification from disk thread side for stream deletion
445                            const Stream::Handle hStream = itVoice->KillImmediately(true);
446                            if (hStream != Stream::INVALID_HANDLE) { // voice actually used a stream
447                                iPendingStreamDeletions++;
448                            }
449                            //NOTE: maybe we should call FreeVoice() here, shouldn't cause a harm though I think, since the voices should be freed by RenderActiveVoices() in the render loop, they are probably just freed a bit later than they could/should be
450                        }
451                    }
452                }
453                // make sure the region is not yet on the list
454                bool bAlreadySuspended = false;
455                RTList< ::gig::Region*>::Iterator iter = SuspendedRegions.first();
456                RTList< ::gig::Region*>::Iterator end  = SuspendedRegions.end();
457                for (; iter != end; ++iter) { // iterate through all suspended regions
458                    if (*iter == pPendingRegionSuspension) { // found
459                        bAlreadySuspended = true;
460                        dmsg(1,("gig::Engine: attempt to suspend an already suspended region !!!\n"));
461                        break;
462                    }
463                }
464                if (!bAlreadySuspended) {
465                    // put the region on the list of suspended regions
466                    RTList< ::gig::Region*>::Iterator iter = SuspendedRegions.allocAppend();
467                    if (iter) {
468                        *iter = pPendingRegionSuspension;
469                    } else std::cerr << "gig::Engine: Could not suspend Region, list is full. This is a bug!!!\n" << std::flush;
470                }
471                // free request slot for next caller (and to make sure that
472                // we're not going to process the same request in the next cycle)
473                pPendingRegionSuspension = NULL;
474                // if no disk stream deletions are pending, awaken other side, as
475                // we're done in this case
476                if (!iPendingStreamDeletions) SuspensionChangeOngoing.Set(false);
477            }
478    
479            // process request for resuming one region
480            if (pPendingRegionResumption) {
481                // remove region from the list of suspended regions
482                RTList< ::gig::Region*>::Iterator iter = SuspendedRegions.first();
483                RTList< ::gig::Region*>::Iterator end  = SuspendedRegions.end();
484                for (; iter != end; ++iter) { // iterate through all suspended regions
485                    if (*iter == pPendingRegionResumption) { // found
486                        SuspendedRegions.free(iter);
487                        break; // done
488                    }
489                }
490                // free request slot for next caller
491                pPendingRegionResumption = NULL;
492                // awake other side as we're done
493                SuspensionChangeOngoing.Set(false);
494            }
495        }
496    
497        /**
498         * Called by the engine's (audio) thread once per cycle to check if
499         * streams of voices that were killed due to suspension request have
500         * finally really been deleted by the disk thread.
501         */
502        void Engine::ProcessPendingStreamDeletions() {
503            if (!iPendingStreamDeletions) return;
504            //TODO: or shall we better store a list with stream handles instead of a scalar amount of streams to be deleted? might be safer
505            while (
506                iPendingStreamDeletions &&
507                pDiskThread->AskForDeletedStream() != Stream::INVALID_HANDLE
508            ) iPendingStreamDeletions--;
509            // just for safety ...
510            while (pDiskThread->AskForDeletedStream() != Stream::INVALID_HANDLE);
511            // now that all disk streams are deleted, awake other side as
512            // we're finally done with suspending the requested region
513            if (!iPendingStreamDeletions) SuspensionChangeOngoing.Set(false);
514        }
515    
516        /**
517         * Returns @c true if the given region is currently set to be suspended
518         * from being used, @c false otherwise.
519         */
520        bool Engine::RegionSuspended(::gig::Region* pRegion) {
521            if (SuspendedRegions.isEmpty()) return false;
522            //TODO: or shall we use a sorted container instead of the RTList? might be faster ... or trivial ;-)
523            RTList< ::gig::Region*>::Iterator iter = SuspendedRegions.first();
524            RTList< ::gig::Region*>::Iterator end  = SuspendedRegions.end();
525            for (; iter != end; ++iter)  // iterate through all suspended regions
526                if (*iter == pRegion) return true;
527            return false;
528      }      }
529    
530      /**      /**
# Line 298  namespace LinuxSampler { namespace gig { Line 548  namespace LinuxSampler { namespace gig {
548       *                  current audio cycle       *                  current audio cycle
549       */       */
550      void Engine::ImportEvents(uint Samples) {      void Engine::ImportEvents(uint Samples) {
551          RingBuffer<Event>::NonVolatileReader eventQueueReader = pEventQueue->get_non_volatile_reader();          RingBuffer<Event,false>::NonVolatileReader eventQueueReader = pEventQueue->get_non_volatile_reader();
552          Event* pEvent;          Event* pEvent;
553          while (true) {          while (true) {
554              // get next event from input event queue              // get next event from input event queue
# Line 321  namespace LinuxSampler { namespace gig { Line 571  namespace LinuxSampler { namespace gig {
571      }      }
572    
573      /**      /**
574       *  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.
575       *  calculated audio data of all voices of this engine will be placed into       * The engine will iterate through all engine channels and render audio
576       *  the engine's audio sum buffer which has to be copied and eventually be       * for each engine channel independently. The calculated audio data of
577       *  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
578       *  AlsaIO or JackIO) right after.       * buffers of the respective audio output device, connected to the
579         * respective engine channel.
580       *       *
581       *  @param Samples - number of sample points to be rendered       *  @param Samples - number of sample points to be rendered
582       *  @returns       0 on success       *  @returns       0 on success
583       */       */
584      int Engine::RenderAudio(uint Samples) {      int Engine::RenderAudio(uint Samples) {
585          dmsg(5,("RenderAudio(Samples=%d)\n", Samples));          dmsg(8,("RenderAudio(Samples=%d)\n", Samples));
586    
587          // return if engine disabled          // return if engine disabled
588          if (EngineDisabled.Pop()) {          if (EngineDisabled.Pop()) {
589              dmsg(5,("gig::Engine: engine disabled (val=%d)\n",EngineDisabled.GetUnsafe()));              dmsg(5,("gig::Engine: engine disabled (val=%d)\n",EngineDisabled.GetUnsafe()));
590                EngineDisabled.RttDone();
591              return 0;              return 0;
592          }          }
593    
594            // process requests for suspending / resuming regions (i.e. to avoid
595            // crashes while these regions are modified by an instrument editor)
596            ProcessSuspensionsChanges();
597    
598          // 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)
599          pEventGenerator->UpdateFragmentTime(Samples);          pEventGenerator->UpdateFragmentTime(Samples);
600    
601          // We only allow a maximum of CONFIG_MAX_VOICES voices to be spawned          // We only allow the given maximum number of voices to be spawned
602          // in each audio fragment. All subsequent request for spawning new          // in each audio fragment. All subsequent request for spawning new
603          // voices in the same audio fragment will be ignored.          // voices in the same audio fragment will be ignored.
604          VoiceSpawnsLeft = CONFIG_MAX_VOICES;          VoiceSpawnsLeft = MaxVoices();
605    
606          // get all events from the engine's global input event queue which belong to the current fragment          // get all events from the engine's global input event queue which belong to the current fragment
607          // (these are usually just SysEx messages)          // (these are usually just SysEx messages)
# Line 368  namespace LinuxSampler { namespace gig { Line 624  namespace LinuxSampler { namespace gig {
624          // reset internal voice counter (just for statistic of active voices)          // reset internal voice counter (just for statistic of active voices)
625          ActiveVoiceCountTemp = 0;          ActiveVoiceCountTemp = 0;
626    
627            // handle instrument change commands
628            bool instrumentChanged = false;
629            for (int i = 0; i < engineChannels.size(); i++) {
630                EngineChannel* pEngineChannel = engineChannels[i];
631    
632                // as we're going to (carefully) write some status to the
633                // synchronized struct, we cast away the const
634                EngineChannel::instrument_change_command_t& cmd =
635                    const_cast<EngineChannel::instrument_change_command_t&>(pEngineChannel->InstrumentChangeCommandReader.Lock());
636    
637                pEngineChannel->pDimRegionsInUse = cmd.pDimRegionsInUse;
638                pEngineChannel->pDimRegionsInUse->clear();
639    
640                if (cmd.bChangeInstrument) {
641                    // change instrument
642                    dmsg(5,("Engine: instrument change command received\n"));
643                    cmd.bChangeInstrument = false;
644                    pEngineChannel->pInstrument = cmd.pInstrument;
645                    instrumentChanged = true;
646    
647                    // Iterate through all active voices and mark them as
648                    // "orphans", which means that the dimension regions
649                    // and samples they use should be released to the
650                    // instrument resource manager when the voices die.
651                    int i = 0;
652                    RTList<uint>::Iterator iuiKey = pEngineChannel->pActiveKeys->first();
653                    RTList<uint>::Iterator end    = pEngineChannel->pActiveKeys->end();
654                    while (iuiKey != end) { // iterate through all active keys
655                        midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[*iuiKey];
656                        ++iuiKey;
657    
658                        RTList<Voice>::Iterator itVoice     = pKey->pActiveVoices->first();
659                        RTList<Voice>::Iterator itVoicesEnd = pKey->pActiveVoices->end();
660                        for (; itVoice != itVoicesEnd; ++itVoice) { // iterate through all voices on this key
661                            itVoice->Orphan = true;
662                        }
663                    }
664                }
665            }
666            if (instrumentChanged) {
667                //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
668                ResetSuspendedRegions();
669            }
670    
671          // handle events on all engine channels          // handle events on all engine channels
672          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  
673              ProcessEvents(engineChannels[i], Samples);              ProcessEvents(engineChannels[i], Samples);
674          }          }
675    
676          // render all 'normal', active voices on all engine channels          // render all 'normal', active voices on all engine channels
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              RenderActiveVoices(engineChannels[i], Samples);              RenderActiveVoices(engineChannels[i], Samples);
679          }          }
680    
681          // 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
682          RenderStolenVoices(Samples);          RenderStolenVoices(Samples);
683    
684            // handle audio routing for engine channels with FX sends
685            for (int i = 0; i < engineChannels.size(); i++) {
686                if (engineChannels[i]->fxSends.empty()) continue; // ignore if no FX sends
687                RouteAudio(engineChannels[i], Samples);
688            }
689    
690          // handle cleanup on all engine channels for the next audio fragment          // handle cleanup on all engine channels for the next audio fragment
691          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  
692              PostProcess(engineChannels[i]);              PostProcess(engineChannels[i]);
693          }          }
694    
# Line 397  namespace LinuxSampler { namespace gig { Line 700  namespace LinuxSampler { namespace gig {
700          pVoiceStealingQueue->clear();          pVoiceStealingQueue->clear();
701    
702          // just some statistics about this engine instance          // just some statistics about this engine instance
703          ActiveVoiceCount = ActiveVoiceCountTemp;          SetVoiceCount(ActiveVoiceCountTemp);
704          if (ActiveVoiceCount > ActiveVoiceCountMax) ActiveVoiceCountMax = ActiveVoiceCount;          if (VoiceCount() > ActiveVoiceCountMax) ActiveVoiceCountMax = VoiceCount();
705    
706            // in case regions were previously suspended and we killed voices
707            // with disk streams due to that, check if those streams have finally
708            // been deleted by the disk thread
709            if (iPendingStreamDeletions) ProcessPendingStreamDeletions();
710    
711            for (int i = 0; i < engineChannels.size(); i++) {
712                engineChannels[i]->InstrumentChangeCommandReader.Unlock();
713            }
714          FrameTime += Samples;          FrameTime += Samples;
715    
716            EngineDisabled.RttDone();
717          return 0;          return 0;
718      }      }
719    
# Line 467  namespace LinuxSampler { namespace gig { Line 779  namespace LinuxSampler { namespace gig {
779          if (pEngineChannel->GetMute()) return; // skip if sampler channel is muted          if (pEngineChannel->GetMute()) return; // skip if sampler channel is muted
780          #endif          #endif
781    
782            uint voiceCount = 0;
783            uint streamCount = 0;
784          RTList<uint>::Iterator iuiKey = pEngineChannel->pActiveKeys->first();          RTList<uint>::Iterator iuiKey = pEngineChannel->pActiveKeys->first();
785          RTList<uint>::Iterator end    = pEngineChannel->pActiveKeys->end();          RTList<uint>::Iterator end    = pEngineChannel->pActiveKeys->end();
786          while (iuiKey != end) { // iterate through all active keys          while (iuiKey != end) { // iterate through all active keys
# Line 478  namespace LinuxSampler { namespace gig { Line 792  namespace LinuxSampler { namespace gig {
792              for (; itVoice != itVoicesEnd; ++itVoice) { // iterate through all voices on this key              for (; itVoice != itVoicesEnd; ++itVoice) { // iterate through all voices on this key
793                  // now render current voice                  // now render current voice
794                  itVoice->Render(Samples);                  itVoice->Render(Samples);
795                  if (itVoice->IsActive()) ActiveVoiceCountTemp++; // still active                  if (itVoice->IsActive()) { // still active
796                  else { // voice reached end, is now inactive                      if (!itVoice->Orphan) {
797                            *(pEngineChannel->pDimRegionsInUse->allocAppend()) = itVoice->pDimRgn;
798                        }
799                        ActiveVoiceCountTemp++;
800                        voiceCount++;
801    
802                        if (itVoice->PlaybackState == Voice::playback_state_disk) {
803                            if ((itVoice->DiskStreamRef).State != Stream::state_unused) streamCount++;
804                        }
805                    }  else { // voice reached end, is now inactive
806                      FreeVoice(pEngineChannel, itVoice); // remove voice from the list of active voices                      FreeVoice(pEngineChannel, itVoice); // remove voice from the list of active voices
807                  }                  }
808              }              }
809          }          }
810    
811            pEngineChannel->SetVoiceCount(voiceCount);
812            pEngineChannel->SetDiskStreamCount(streamCount);
813      }      }
814    
815      /**      /**
# Line 504  namespace LinuxSampler { namespace gig { Line 830  namespace LinuxSampler { namespace gig {
830          RTList<Event>::Iterator end               = pVoiceStealingQueue->end();          RTList<Event>::Iterator end               = pVoiceStealingQueue->end();
831          for (; itVoiceStealEvent != end; ++itVoiceStealEvent) {          for (; itVoiceStealEvent != end; ++itVoiceStealEvent) {
832              EngineChannel* pEngineChannel = (EngineChannel*) itVoiceStealEvent->pEngineChannel;              EngineChannel* pEngineChannel = (EngineChannel*) itVoiceStealEvent->pEngineChannel;
833                if (!pEngineChannel->pInstrument) continue; // ignore if no instrument loaded
834              Pool<Voice>::Iterator itNewVoice =              Pool<Voice>::Iterator itNewVoice =
835                  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);
836              if (itNewVoice) {              if (itNewVoice) {
837                  itNewVoice->Render(Samples);                  itNewVoice->Render(Samples);
838                  if (itNewVoice->IsActive()) ActiveVoiceCountTemp++; // still active                  if (itNewVoice->IsActive()) { // still active
839                  else { // voice reached end, is now inactive                      *(pEngineChannel->pDimRegionsInUse->allocAppend()) = itNewVoice->pDimRgn;
840                        ActiveVoiceCountTemp++;
841                        pEngineChannel->SetVoiceCount(pEngineChannel->GetVoiceCount() + 1);
842    
843                        if (itNewVoice->PlaybackState == Voice::playback_state_disk) {
844                            if (itNewVoice->DiskStreamRef.State != Stream::state_unused) {
845                                pEngineChannel->SetDiskStreamCount(pEngineChannel->GetDiskStreamCount() + 1);
846                            }
847                        }
848                    } else { // voice reached end, is now inactive
849                      FreeVoice(pEngineChannel, itNewVoice); // remove voice from the list of active voices                      FreeVoice(pEngineChannel, itNewVoice); // remove voice from the list of active voices
850                  }                  }
851              }              }
# Line 523  namespace LinuxSampler { namespace gig { Line 859  namespace LinuxSampler { namespace gig {
859      }      }
860    
861      /**      /**
862         * Will be called in case the respective engine channel sports FX send
863         * channels. In this particular case, engine channel local buffers are
864         * used to render and mix all voices to. This method is responsible for
865         * copying the audio data from those local buffers to the master audio
866         * output channels as well as to the FX send audio output channels with
867         * their respective FX send levels.
868         *
869         * @param pEngineChannel - engine channel from which audio should be
870         *                         routed
871         * @param Samples        - amount of sample points to be routed in
872         *                         this audio fragment cycle
873         */
874        void Engine::RouteAudio(EngineChannel* pEngineChannel, uint Samples) {
875            // route dry signal
876            {
877                AudioChannel* pDstL = pAudioOutputDevice->Channel(pEngineChannel->AudioDeviceChannelLeft);
878                AudioChannel* pDstR = pAudioOutputDevice->Channel(pEngineChannel->AudioDeviceChannelRight);
879                pEngineChannel->pChannelLeft->MixTo(pDstL, Samples);
880                pEngineChannel->pChannelRight->MixTo(pDstR, Samples);
881            }
882            // route FX send signal
883            {
884                for (int iFxSend = 0; iFxSend < pEngineChannel->GetFxSendCount(); iFxSend++) {
885                    FxSend* pFxSend = pEngineChannel->GetFxSend(iFxSend);
886                    for (int iChan = 0; iChan < 2; ++iChan) {
887                        AudioChannel* pSource =
888                            (iChan)
889                                ? pEngineChannel->pChannelRight
890                                : pEngineChannel->pChannelLeft;
891                        const int iDstChan = pFxSend->DestinationChannel(iChan);
892                        if (iDstChan < 0) {
893                            dmsg(1,("Engine::RouteAudio() Error: invalid FX send (%s) destination channel (%d->%d)", ((iChan) ? "R" : "L"), iChan, iDstChan));
894                            goto channel_cleanup;
895                        }
896                        AudioChannel* pDstChan = NULL;
897                        if (pFxSend->DestinationMasterEffectChain() >= 0) { // fx send routed to an internal master effect
898                            EffectChain* pEffectChain =
899                                pAudioOutputDevice->MasterEffectChain(
900                                    pFxSend->DestinationMasterEffectChain()
901                                );
902                            if (!pEffectChain) {
903                                dmsg(1,("Engine::RouteAudio() Error: invalid FX send (%s) destination effect chain %d", ((iChan) ? "R" : "L"), pFxSend->DestinationMasterEffectChain()));
904                                goto channel_cleanup;
905                            }
906                            Effect* pEffect =
907                                pEffectChain->GetEffect(
908                                    pFxSend->DestinationMasterEffect()
909                                );
910                            if (!pEffect) {
911                                dmsg(1,("Engine::RouteAudio() Error: invalid FX send (%s) destination effect %d of effect chain %d", ((iChan) ? "R" : "L"), pFxSend->DestinationMasterEffect(), pFxSend->DestinationMasterEffectChain()));
912                                goto channel_cleanup;
913                            }
914                            pDstChan = pEffect->InputChannel(iDstChan);
915                        } else { // FX send routed directly to an audio output channel
916                            pDstChan = pAudioOutputDevice->Channel(iDstChan);
917                        }
918                        if (!pDstChan) {
919                            dmsg(1,("Engine::RouteAudio() Error: invalid FX send (%s) destination channel (%d->%d)", ((iChan) ? "R" : "L"), iChan, iDstChan));
920                            goto channel_cleanup;
921                        }
922                        pSource->MixTo(pDstChan, Samples, pFxSend->Level());
923                    }
924                }
925            }
926            channel_cleanup:
927            // reset buffers with silence (zero out) for the next audio cycle
928            pEngineChannel->pChannelLeft->Clear();
929            pEngineChannel->pChannelRight->Clear();
930        }
931    
932        /**
933       * Free all keys which have turned inactive in this audio fragment, from       * Free all keys which have turned inactive in this audio fragment, from
934       * 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
935       * channel.       * channel.
# Line 562  namespace LinuxSampler { namespace gig { Line 969  namespace LinuxSampler { namespace gig {
969       *       *
970       *  @param pData - pointer to sysex data       *  @param pData - pointer to sysex data
971       *  @param Size  - lenght of sysex data (in bytes)       *  @param Size  - lenght of sysex data (in bytes)
972         *  @param pSender - the MIDI input port on which the SysEx message was
973         *                   received
974       */       */
975      void Engine::SendSysex(void* pData, uint Size) {      void Engine::SendSysex(void* pData, uint Size, MidiInputPort* pSender) {
976          Event event             = pEventGenerator->CreateEvent();          Event event             = pEventGenerator->CreateEvent();
977          event.Type              = Event::type_sysex;          event.Type              = Event::type_sysex;
978          event.Param.Sysex.Size  = Size;          event.Param.Sysex.Size  = Size;
979          event.pEngineChannel    = NULL; // as Engine global event          event.pEngineChannel    = NULL; // as Engine global event
980            event.pMidiInputPort    = pSender;
981          if (pEventQueue->write_space() > 0) {          if (pEventQueue->write_space() > 0) {
982              if (pSysexBuffer->write_space() >= Size) {              if (pSysexBuffer->write_space() >= Size) {
983                  // copy sysex data to input buffer                  // copy sysex data to input buffer
# Line 599  namespace LinuxSampler { namespace gig { Line 1009  namespace LinuxSampler { namespace gig {
1009          if (pEngineChannel->GetMute()) return; // skip if sampler channel is muted          if (pEngineChannel->GetMute()) return; // skip if sampler channel is muted
1010          #endif          #endif
1011    
1012            if (!pEngineChannel->pInstrument) return; // ignore if no instrument loaded
1013    
1014            //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
1015            itNoteOnEvent->Param.Note.Key += pEngineChannel->GlobalTranspose;
1016    
1017          const int key = itNoteOnEvent->Param.Note.Key;          const int key = itNoteOnEvent->Param.Note.Key;
1018          midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[key];          midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[key];
1019    
# Line 634  namespace LinuxSampler { namespace gig { Line 1049  namespace LinuxSampler { namespace gig {
1049          {          {
1050              const ::gig::Instrument* pInstrument = pEngineChannel->pInstrument;              const ::gig::Instrument* pInstrument = pEngineChannel->pInstrument;
1051              if (key >= pInstrument->DimensionKeyRange.low && key <= pInstrument->DimensionKeyRange.high)              if (key >= pInstrument->DimensionKeyRange.low && key <= pInstrument->DimensionKeyRange.high)
1052                  pEngineChannel->CurrentKeyDimension = ((key - pInstrument->DimensionKeyRange.low) * 128) /                  pEngineChannel->CurrentKeyDimension = float(key - pInstrument->DimensionKeyRange.low) /
1053                      (pInstrument->DimensionKeyRange.high - pInstrument->DimensionKeyRange.low + 1);                      (pInstrument->DimensionKeyRange.high - pInstrument->DimensionKeyRange.low + 1);
1054          }          }
1055    
# Line 656  namespace LinuxSampler { namespace gig { Line 1071  namespace LinuxSampler { namespace gig {
1071          {          {
1072              // first, get total amount of required voices (dependant on amount of layers)              // first, get total amount of required voices (dependant on amount of layers)
1073              ::gig::Region* pRegion = pEngineChannel->pInstrument->GetRegion(itNoteOnEventOnKeyList->Param.Note.Key);              ::gig::Region* pRegion = pEngineChannel->pInstrument->GetRegion(itNoteOnEventOnKeyList->Param.Note.Key);
1074              if (pRegion) {              if (pRegion && !RegionSuspended(pRegion)) {
1075                  int voicesRequired = pRegion->Layers;                  int voicesRequired = pRegion->Layers;
1076                  // now launch the required amount of voices                  // now launch the required amount of voices
1077                  for (int i = 0; i < voicesRequired; i++)                  for (int i = 0; i < voicesRequired; i++)
# Line 686  namespace LinuxSampler { namespace gig { Line 1101  namespace LinuxSampler { namespace gig {
1101          if (pEngineChannel->GetMute()) return; // skip if sampler channel is muted          if (pEngineChannel->GetMute()) return; // skip if sampler channel is muted
1102          #endif          #endif
1103    
1104            //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
1105            itNoteOffEvent->Param.Note.Key += pEngineChannel->GlobalTranspose;
1106    
1107          const int iKey = itNoteOffEvent->Param.Note.Key;          const int iKey = itNoteOffEvent->Param.Note.Key;
1108          midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[iKey];          midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[iKey];
1109          pKey->KeyPressed = false; // the MIDI key was now released          pKey->KeyPressed = false; // the MIDI key was now released
# Line 696  namespace LinuxSampler { namespace gig { Line 1114  namespace LinuxSampler { namespace gig {
1114          bool bShouldRelease = pKey->Active && ShouldReleaseVoice(pEngineChannel, itNoteOffEventOnKeyList->Param.Note.Key);          bool bShouldRelease = pKey->Active && ShouldReleaseVoice(pEngineChannel, itNoteOffEventOnKeyList->Param.Note.Key);
1115    
1116          // in case Solo Mode is enabled, kill all voices on this key and respawn a voice on the highest pressed key (if any)          // in case Solo Mode is enabled, kill all voices on this key and respawn a voice on the highest pressed key (if any)
1117          if (pEngineChannel->SoloMode) { //TODO: this feels like too much code just for handling solo mode :P          if (pEngineChannel->SoloMode && pEngineChannel->pInstrument) { //TODO: this feels like too much code just for handling solo mode :P
1118              bool bOtherKeysPressed = false;              bool bOtherKeysPressed = false;
1119              if (iKey == pEngineChannel->SoloKey) {              if (iKey == pEngineChannel->SoloKey) {
1120                  pEngineChannel->SoloKey = -1;                  pEngineChannel->SoloKey = -1;
# Line 759  namespace LinuxSampler { namespace gig { Line 1177  namespace LinuxSampler { namespace gig {
1177              itNoteOffEventOnKeyList->Type = Event::type_release; // transform event type              itNoteOffEventOnKeyList->Type = Event::type_release; // transform event type
1178    
1179              // spawn release triggered voice(s) if needed              // spawn release triggered voice(s) if needed
1180              if (pKey->ReleaseTrigger) {              if (pKey->ReleaseTrigger && pEngineChannel->pInstrument) {
1181                  // first, get total amount of required voices (dependant on amount of layers)                  // first, get total amount of required voices (dependant on amount of layers)
1182                  ::gig::Region* pRegion = pEngineChannel->pInstrument->GetRegion(itNoteOffEventOnKeyList->Param.Note.Key);                  ::gig::Region* pRegion = pEngineChannel->pInstrument->GetRegion(itNoteOffEventOnKeyList->Param.Note.Key);
1183                  if (pRegion) {                  if (pRegion) {
# Line 862  namespace LinuxSampler { namespace gig { Line 1280  namespace LinuxSampler { namespace gig {
1280                      DimValues[i] = itNoteOnEvent->Param.Note.Velocity;                      DimValues[i] = itNoteOnEvent->Param.Note.Velocity;
1281                      break;                      break;
1282                  case ::gig::dimension_channelaftertouch:                  case ::gig::dimension_channelaftertouch:
1283                      DimValues[i] = 0; //TODO: we currently ignore this dimension                      DimValues[i] = pEngineChannel->ControllerTable[128];
1284                      break;                      break;
1285                  case ::gig::dimension_releasetrigger:                  case ::gig::dimension_releasetrigger:
1286                      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;
1287                      DimValues[i] = (uint) ReleaseTriggerVoice;                      DimValues[i] = (uint) ReleaseTriggerVoice;
1288                      break;                      break;
1289                  case ::gig::dimension_keyboard:                  case ::gig::dimension_keyboard:
1290                      DimValues[i] = (uint) pEngineChannel->CurrentKeyDimension;                      DimValues[i] = (uint) (pEngineChannel->CurrentKeyDimension * pRegion->pDimensionDefinitions[i].zones);
1291                      break;                      break;
1292                  case ::gig::dimension_roundrobin:                  case ::gig::dimension_roundrobin:
1293                      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 954  namespace LinuxSampler { namespace gig { Line 1372  namespace LinuxSampler { namespace gig {
1372                      std::cerr << "gig::Engine::LaunchVoice() Error: Unknown dimension\n" << std::flush;                      std::cerr << "gig::Engine::LaunchVoice() Error: Unknown dimension\n" << std::flush;
1373              }              }
1374          }          }
1375    
1376            // return if this is a release triggered voice and there is no
1377            // releasetrigger dimension (could happen if an instrument
1378            // change has occured between note on and off)
1379            if (ReleaseTriggerVoice && VoiceType != Voice::type_release_trigger) return Pool<Voice>::Iterator();
1380    
1381          ::gig::DimensionRegion* pDimRgn = pRegion->GetDimensionRegionByValue(DimValues);          ::gig::DimensionRegion* pDimRgn = pRegion->GetDimensionRegionByValue(DimValues);
1382    
1383          // no need to continue if sample is silent          // no need to continue if sample is silent
# Line 1183  namespace LinuxSampler { namespace gig { Line 1607  namespace LinuxSampler { namespace gig {
1607    
1608              uint keygroup = itVoice->KeyGroup;              uint keygroup = itVoice->KeyGroup;
1609    
1610                // if the sample and dimension region belong to an
1611                // instrument that is unloaded, tell the disk thread to
1612                // release them
1613                if (itVoice->Orphan) {
1614                    pDiskThread->OrderDeletionOfDimreg(itVoice->pDimRgn);
1615                }
1616    
1617              // free the voice object              // free the voice object
1618              pVoicePool->free(itVoice);              pVoicePool->free(itVoice);
1619    
# Line 1224  namespace LinuxSampler { namespace gig { Line 1655  namespace LinuxSampler { namespace gig {
1655      void Engine::ProcessControlChange(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itControlChangeEvent) {      void Engine::ProcessControlChange(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itControlChangeEvent) {
1656          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));
1657    
1658            // handle the "control triggered" MIDI rule: a control change
1659            // event can trigger a new note on or note off event
1660            if (pEngineChannel->pInstrument) {
1661    
1662                ::gig::MidiRule* rule;
1663                for (int i = 0 ; (rule = pEngineChannel->pInstrument->GetMidiRule(i)) ; i++) {
1664    
1665                    if (::gig::MidiRuleCtrlTrigger* ctrlTrigger =
1666                        dynamic_cast< ::gig::MidiRuleCtrlTrigger*>(rule)) {
1667                        if (itControlChangeEvent->Param.CC.Controller ==
1668                            ctrlTrigger->ControllerNumber) {
1669    
1670                            uint8_t oldCCValue = pEngineChannel->ControllerTable[
1671                                itControlChangeEvent->Param.CC.Controller];
1672                            uint8_t newCCValue = itControlChangeEvent->Param.CC.Value;
1673    
1674                            for (int i = 0 ; i < ctrlTrigger->Triggers ; i++) {
1675                                ::gig::MidiRuleCtrlTrigger::trigger_t* pTrigger =
1676                                      &ctrlTrigger->pTriggers[i];
1677    
1678                                // check if the controller has passed the
1679                                // trigger point in the right direction
1680                                if ((pTrigger->Descending &&
1681                                     oldCCValue > pTrigger->TriggerPoint &&
1682                                     newCCValue <= pTrigger->TriggerPoint) ||
1683                                    (!pTrigger->Descending &&
1684                                     oldCCValue < pTrigger->TriggerPoint &&
1685                                     newCCValue >= pTrigger->TriggerPoint)) {
1686    
1687                                    RTList<Event>::Iterator itNewEvent = pGlobalEvents->allocAppend();
1688                                    if (itNewEvent) {
1689                                        *itNewEvent = *itControlChangeEvent;
1690                                        itNewEvent->Param.Note.Key = pTrigger->Key;
1691    
1692                                        if (pTrigger->NoteOff || pTrigger->Velocity == 0) {
1693                                            itNewEvent->Type = Event::type_note_off;
1694                                            itNewEvent->Param.Note.Velocity = 100;
1695    
1696                                            ProcessNoteOff(pEngineChannel, itNewEvent);
1697                                        } else {
1698                                            itNewEvent->Type = Event::type_note_on;
1699                                            //TODO: if Velocity is 255, the triggered velocity should
1700                                            // depend on how fast the controller is moving
1701                                            itNewEvent->Param.Note.Velocity =
1702                                                pTrigger->Velocity == 255 ? 100 :
1703                                                pTrigger->Velocity;
1704    
1705                                            ProcessNoteOn(pEngineChannel, itNewEvent);
1706                                        }
1707                                    }
1708                                    else dmsg(1,("Event pool emtpy!\n"));
1709                                }
1710                            }
1711                        }
1712                    }
1713                }
1714            }
1715    
1716          // update controller value in the engine channel's controller table          // update controller value in the engine channel's controller table
1717          pEngineChannel->ControllerTable[itControlChangeEvent->Param.CC.Controller] = itControlChangeEvent->Param.CC.Value;          pEngineChannel->ControllerTable[itControlChangeEvent->Param.CC.Controller] = itControlChangeEvent->Param.CC.Value;
1718    
1719            // handle hard coded MIDI controllers
1720          switch (itControlChangeEvent->Param.CC.Controller) {          switch (itControlChangeEvent->Param.CC.Controller) {
1721              case 5: { // portamento time              case 5: { // portamento time
1722                  pEngineChannel->PortamentoTime = (float) itControlChangeEvent->Param.CC.Value / 127.0f * (float) CONFIG_PORTAMENTO_TIME_MAX + (float) CONFIG_PORTAMENTO_TIME_MIN;                  pEngineChannel->PortamentoTime = (float) itControlChangeEvent->Param.CC.Value / 127.0f * (float) CONFIG_PORTAMENTO_TIME_MAX + (float) CONFIG_PORTAMENTO_TIME_MIN;
1723                  break;                  break;
1724              }              }
1725                case 6: { // data entry (currently only used for RPN controllers)
1726                    if (pEngineChannel->GetMidiRpnController() == 2) { // coarse tuning in half tones
1727                        int transpose = (int) itControlChangeEvent->Param.CC.Value - 64;
1728                        // limit to +- two octaves for now
1729                        transpose = RTMath::Min(transpose,  24);
1730                        transpose = RTMath::Max(transpose, -24);
1731                        pEngineChannel->GlobalTranspose = transpose;
1732                        // workaround, so we won't have hanging notes
1733                        ReleaseAllVoices(pEngineChannel, itControlChangeEvent);
1734                    }
1735                    // to avoid other MIDI CC #6 messages to be misenterpreted as RPN controller data
1736                    pEngineChannel->ResetMidiRpnController();
1737                    break;
1738                }
1739              case 7: { // volume              case 7: { // volume
1740                  //TODO: not sample accurate yet                  //TODO: not sample accurate yet
1741                  pEngineChannel->GlobalVolume = VolumeCurve[itControlChangeEvent->Param.CC.Value] *  CONFIG_GLOBAL_ATTENUATION;                  pEngineChannel->MidiVolume = VolumeCurve[itControlChangeEvent->Param.CC.Value];
1742                  pEngineChannel->bStatusChanged = true; // engine channel status has changed, so set notify flag                  pEngineChannel->bStatusChanged = true; // engine channel status has changed, so set notify flag
1743                  break;                  break;
1744              }              }
# Line 1242  namespace LinuxSampler { namespace gig { Line 1746  namespace LinuxSampler { namespace gig {
1746                  //TODO: not sample accurate yet                  //TODO: not sample accurate yet
1747                  pEngineChannel->GlobalPanLeft  = PanCurve[128 - itControlChangeEvent->Param.CC.Value];                  pEngineChannel->GlobalPanLeft  = PanCurve[128 - itControlChangeEvent->Param.CC.Value];
1748                  pEngineChannel->GlobalPanRight = PanCurve[itControlChangeEvent->Param.CC.Value];                  pEngineChannel->GlobalPanRight = PanCurve[itControlChangeEvent->Param.CC.Value];
1749                    pEngineChannel->iLastPanRequest = itControlChangeEvent->Param.CC.Value;
1750                  break;                  break;
1751              }              }
1752              case 64: { // sustain              case 64: { // sustain
# Line 1292  namespace LinuxSampler { namespace gig { Line 1797  namespace LinuxSampler { namespace gig {
1797                  break;                  break;
1798              }              }
1799              case 65: { // portamento on / off              case 65: { // portamento on / off
1800                  KillAllVoices(pEngineChannel, itControlChangeEvent);                  const bool bPortamento = itControlChangeEvent->Param.CC.Value >= 64;
1801                  pEngineChannel->PortamentoMode = itControlChangeEvent->Param.CC.Value >= 64;                  if (bPortamento != pEngineChannel->PortamentoMode)
1802                        KillAllVoices(pEngineChannel, itControlChangeEvent);
1803                    pEngineChannel->PortamentoMode = bPortamento;
1804                  break;                  break;
1805              }              }
1806              case 66: { // sostenuto              case 66: { // sostenuto
# Line 1336  namespace LinuxSampler { namespace gig { Line 1843  namespace LinuxSampler { namespace gig {
1843                  }                  }
1844                  break;                  break;
1845              }              }
1846                case 100: { // RPN controller LSB
1847                    pEngineChannel->SetMidiRpnControllerLsb(itControlChangeEvent->Param.CC.Value);
1848                    break;
1849                }
1850                case 101: { // RPN controller MSB
1851                    pEngineChannel->SetMidiRpnControllerMsb(itControlChangeEvent->Param.CC.Value);
1852                    break;
1853                }
1854    
1855    
1856              // Channel Mode Messages              // Channel Mode Messages
# Line 1349  namespace LinuxSampler { namespace gig { Line 1864  namespace LinuxSampler { namespace gig {
1864                  break;                  break;
1865              }              }
1866              case 123: { // all notes off              case 123: { // all notes off
1867                    #if CONFIG_PROCESS_ALL_NOTES_OFF
1868                  ReleaseAllVoices(pEngineChannel, itControlChangeEvent);                  ReleaseAllVoices(pEngineChannel, itControlChangeEvent);
1869                    #endif // CONFIG_PROCESS_ALL_NOTES_OFF
1870                  break;                  break;
1871              }              }
1872              case 126: { // mono mode on              case 126: { // mono mode on
1873                  KillAllVoices(pEngineChannel, itControlChangeEvent);                  if (!pEngineChannel->SoloMode)
1874                        KillAllVoices(pEngineChannel, itControlChangeEvent);
1875                  pEngineChannel->SoloMode = true;                  pEngineChannel->SoloMode = true;
1876                  break;                  break;
1877              }              }
1878              case 127: { // poly mode on              case 127: { // poly mode on
1879                  KillAllVoices(pEngineChannel, itControlChangeEvent);                  if (pEngineChannel->SoloMode)
1880                        KillAllVoices(pEngineChannel, itControlChangeEvent);
1881                  pEngineChannel->SoloMode = false;                  pEngineChannel->SoloMode = false;
1882                  break;                  break;
1883              }              }
1884          }          }
1885    
1886            // handle FX send controllers
1887            if (!pEngineChannel->fxSends.empty()) {
1888                for (int iFxSend = 0; iFxSend < pEngineChannel->GetFxSendCount(); iFxSend++) {
1889                    FxSend* pFxSend = pEngineChannel->GetFxSend(iFxSend);
1890                    if (pFxSend->MidiController() == itControlChangeEvent->Param.CC.Controller) {
1891                        pFxSend->SetLevel(itControlChangeEvent->Param.CC.Value);
1892                        pFxSend->SetInfoChanged(true);
1893                    }
1894                }
1895            }
1896      }      }
1897    
1898      /**      /**
# Line 1371  namespace LinuxSampler { namespace gig { Line 1901  namespace LinuxSampler { namespace gig {
1901       *  @param itSysexEvent - sysex data size and time stamp of the sysex event       *  @param itSysexEvent - sysex data size and time stamp of the sysex event
1902       */       */
1903      void Engine::ProcessSysex(Pool<Event>::Iterator& itSysexEvent) {      void Engine::ProcessSysex(Pool<Event>::Iterator& itSysexEvent) {
1904          RingBuffer<uint8_t>::NonVolatileReader reader = pSysexBuffer->get_non_volatile_reader();          RingBuffer<uint8_t,false>::NonVolatileReader reader = pSysexBuffer->get_non_volatile_reader();
1905    
1906          uint8_t exclusive_status, id;          uint8_t exclusive_status, id;
1907          if (!reader.pop(&exclusive_status)) goto free_sysex_data;          if (!reader.pop(&exclusive_status)) goto free_sysex_data;
# Line 1379  namespace LinuxSampler { namespace gig { Line 1909  namespace LinuxSampler { namespace gig {
1909          if (exclusive_status != 0xF0)       goto free_sysex_data;          if (exclusive_status != 0xF0)       goto free_sysex_data;
1910    
1911          switch (id) {          switch (id) {
1912                case 0x7f: { // (Realtime) Universal Sysex (GM Standard)
1913                    uint8_t sysex_channel, sub_id1, sub_id2, val_msb, val_lsb;;
1914                    if (!reader.pop(&sysex_channel)) goto free_sysex_data;
1915                    if (!reader.pop(&sub_id1)) goto free_sysex_data;
1916                    if (!reader.pop(&sub_id2)) goto free_sysex_data;
1917                    if (!reader.pop(&val_lsb)) goto free_sysex_data;
1918                    if (!reader.pop(&val_msb)) goto free_sysex_data;
1919                    //TODO: for now we simply ignore the sysex channel, seldom used anyway
1920                    switch (sub_id1) {
1921                        case 0x04: // Device Control
1922                            switch (sub_id2) {
1923                                case 0x01: { // Master Volume
1924                                    const double volume =
1925                                        double((uint(val_msb)<<7) | uint(val_lsb)) / 16383.0;
1926                                    #if CONFIG_MASTER_VOLUME_SYSEX_BY_PORT
1927                                    // apply volume to all sampler channels that
1928                                    // are connected to the same MIDI input port
1929                                    // this sysex message arrived on
1930                                    for (int i = 0; i < engineChannels.size(); ++i) {
1931                                        EngineChannel* pEngineChannel = engineChannels[i];
1932                                        if (pEngineChannel->GetMidiInputPort() ==
1933                                            itSysexEvent->pMidiInputPort)
1934                                        {
1935                                            pEngineChannel->Volume(volume);
1936                                        }
1937                                    }
1938                                    #else
1939                                    // apply volume globally to the whole sampler
1940                                    GLOBAL_VOLUME = volume;
1941                                    #endif // CONFIG_MASTER_VOLUME_SYSEX_BY_PORT
1942                                    break;
1943                                }
1944                            }
1945                            break;
1946                    }
1947                    break;
1948                }
1949              case 0x41: { // Roland              case 0x41: { // Roland
1950                  dmsg(3,("Roland Sysex\n"));                  dmsg(3,("Roland Sysex\n"));
1951                  uint8_t device_id, model_id, cmd_id;                  uint8_t device_id, model_id, cmd_id;
# Line 1390  namespace LinuxSampler { namespace gig { Line 1957  namespace LinuxSampler { namespace gig {
1957    
1958                  // command address                  // command address
1959                  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)
1960                  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
1961                  if (reader.read(&addr[0], 3) != 3) goto free_sysex_data;                  if (reader.read(&addr[0], 3) != 3) goto free_sysex_data;
1962                  if (addr[0] == 0x40 && addr[1] == 0x00) { // System Parameters                  if (addr[0] == 0x40 && addr[1] == 0x00) { // System Parameters
1963                      dmsg(3,("\tSystem Parameter\n"));                      dmsg(3,("\tSystem Parameter\n"));
1964                        if (addr[2] == 0x7f) { // GS reset
1965                            for (int i = 0; i < engineChannels.size(); ++i) {
1966                                EngineChannel* pEngineChannel = engineChannels[i];
1967                                if (pEngineChannel->GetMidiInputPort() == itSysexEvent->pMidiInputPort) {
1968                                    KillAllVoices(pEngineChannel, itSysexEvent);
1969                                    pEngineChannel->ResetControllers();
1970                                }
1971                            }
1972                        }
1973                  }                  }
1974                  else if (addr[0] == 0x40 && addr[1] == 0x01) { // Common Parameters                  else if (addr[0] == 0x40 && addr[1] == 0x01) { // Common Parameters
1975                      dmsg(3,("\tCommon Parameter\n"));                      dmsg(3,("\tCommon Parameter\n"));
# Line 1415  namespace LinuxSampler { namespace gig { Line 1991  namespace LinuxSampler { namespace gig {
1991                              dmsg(3,("\t\t\tNew scale applied.\n"));                              dmsg(3,("\t\t\tNew scale applied.\n"));
1992                              break;                              break;
1993                          }                          }
1994                            case 0x15: { // chromatic / drumkit mode
1995                                dmsg(3,("\t\tMIDI Instrument Map Switch\n"));
1996                                uint8_t part = addr[1] & 0x0f;
1997                                uint8_t map;
1998                                if (!reader.pop(&map)) goto free_sysex_data;
1999                                for (int i = 0; i < engineChannels.size(); ++i) {
2000                                    EngineChannel* pEngineChannel = engineChannels[i];
2001                                    if (
2002                                        (pEngineChannel->midiChannel == part ||
2003                                         pEngineChannel->midiChannel == midi_chan_all) &&
2004                                         pEngineChannel->GetMidiInputPort() == itSysexEvent->pMidiInputPort
2005                                    ) {
2006                                        try {
2007                                            pEngineChannel->SetMidiInstrumentMap(map);
2008                                        } catch (Exception e) {
2009                                            dmsg(2,("\t\t\tCould not apply MIDI instrument map %d to part %d: %s\n", map, part, e.Message().c_str()));
2010                                            goto free_sysex_data;
2011                                        } catch (...) {
2012                                            dmsg(2,("\t\t\tCould not apply MIDI instrument map %d to part %d (unknown exception)\n", map, part));
2013                                            goto free_sysex_data;
2014                                        }
2015                                    }
2016                                }
2017                                dmsg(3,("\t\t\tApplied MIDI instrument map %d to part %d.\n", map, part));
2018                                break;
2019                            }
2020                      }                      }
2021                  }                  }
2022                  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 1437  namespace LinuxSampler { namespace gig { Line 2039  namespace LinuxSampler { namespace gig {
2039       *                     question       *                     question
2040       * @param DataSize   - size of the GS message data (in bytes)       * @param DataSize   - size of the GS message data (in bytes)
2041       */       */
2042      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) {
2043          RingBuffer<uint8_t>::NonVolatileReader reader = AddrReader;          RingBuffer<uint8_t,false>::NonVolatileReader reader = AddrReader;
2044          uint bytes = 3 /*addr*/ + DataSize;          uint bytes = 3 /*addr*/ + DataSize;
2045          uint8_t addr_and_data[bytes];          uint8_t addr_and_data[bytes];
2046          reader.read(&addr_and_data[0], bytes);          reader.read(&addr_and_data[0], bytes);
# Line 1521  namespace LinuxSampler { namespace gig { Line 2123  namespace LinuxSampler { namespace gig {
2123      }      }
2124    
2125      uint Engine::VoiceCount() {      uint Engine::VoiceCount() {
2126          return ActiveVoiceCount;          return atomic_read(&ActiveVoiceCount);
2127        }
2128    
2129        void Engine::SetVoiceCount(uint Count) {
2130            atomic_set(&ActiveVoiceCount, Count);
2131      }      }
2132    
2133      uint Engine::VoiceCountMax() {      uint Engine::VoiceCountMax() {
2134          return ActiveVoiceCountMax;          return ActiveVoiceCountMax;
2135      }      }
2136    
2137        int Engine::MaxVoices() {
2138            return pVoicePool->poolSize();
2139        }
2140    
2141        void Engine::SetMaxVoices(int iVoices) throw (Exception) {
2142            if (iVoices < 1)
2143                throw Exception("Maximum voices for an engine cannot be set lower than 1");
2144    
2145            SuspendAll();
2146    
2147            // NOTE: we need to clear pDimRegionsInUse before deleting pDimRegionPool,
2148            // otherwise memory corruption will occur if there are active voices (see bug #118)
2149            for (int iChannel = 0; iChannel < engineChannels.size(); iChannel++) {
2150                engineChannels[iChannel]->ClearDimRegionsInUse();
2151            }
2152    
2153            if (pDimRegionPool[0]) delete pDimRegionPool[0];
2154            if (pDimRegionPool[1]) delete pDimRegionPool[1];
2155    
2156            pDimRegionPool[0] = new Pool< ::gig::DimensionRegion*>(iVoices);
2157            pDimRegionPool[1] = new Pool< ::gig::DimensionRegion*>(iVoices);
2158    
2159            for (int iChannel = 0; iChannel < engineChannels.size(); iChannel++) {
2160                engineChannels[iChannel]->ResetDimRegionsInUse();
2161            }
2162    
2163            try {
2164                pVoicePool->resizePool(iVoices);
2165            } catch (...) {
2166                throw Exception("FATAL: Could not resize voice pool!");
2167            }
2168    
2169            for (RTList<Voice>::Iterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) {
2170                iterVoice->SetEngine(this);
2171                iterVoice->pDiskThread = this->pDiskThread;
2172            }
2173            pVoicePool->clear();
2174    
2175            ResumeAll();
2176        }
2177    
2178      bool Engine::DiskStreamSupported() {      bool Engine::DiskStreamSupported() {
2179          return true;          return true;
2180      }      }
2181    
2182      uint Engine::DiskStreamCount() {      uint Engine::DiskStreamCount() {
2183          return (pDiskThread) ? pDiskThread->ActiveStreamCount : 0;          return (pDiskThread) ? pDiskThread->GetActiveStreamCount() : 0;
2184      }      }
2185    
2186      uint Engine::DiskStreamCountMax() {      uint Engine::DiskStreamCountMax() {
2187          return (pDiskThread) ? pDiskThread->ActiveStreamCountMax : 0;          return (pDiskThread) ? pDiskThread->ActiveStreamCountMax : 0;
2188      }      }
2189    
2190        int Engine::MaxDiskStreams() {
2191            return iMaxDiskStreams;
2192        }
2193    
2194        void Engine::SetMaxDiskStreams(int iStreams) throw (Exception) {
2195            if (iStreams < 0)
2196                throw Exception("Maximum disk streams for an engine cannot be set lower than 0");
2197    
2198            SuspendAll();
2199    
2200            iMaxDiskStreams = iStreams;
2201    
2202            // reconnect to audio output device, because that will automatically
2203            // recreate the disk thread with the required amount of streams
2204            if (pAudioOutputDevice) Connect(pAudioOutputDevice);
2205    
2206            ResumeAll();
2207        }
2208    
2209      String Engine::DiskStreamBufferFillBytes() {      String Engine::DiskStreamBufferFillBytes() {
2210          return pDiskThread->GetBufferFillBytes();          return pDiskThread->GetBufferFillBytes();
2211      }      }
# Line 1553  namespace LinuxSampler { namespace gig { Line 2219  namespace LinuxSampler { namespace gig {
2219      }      }
2220    
2221      String Engine::Description() {      String Engine::Description() {
2222          return "Gigasampler Engine";          return "Gigasampler Format Engine";
2223      }      }
2224    
2225      String Engine::Version() {      String Engine::Version() {
2226          String s = "$Revision: 1.58 $";          String s = "$Revision: 1.104 $";
2227          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
2228      }      }
2229    
2230        InstrumentManager* Engine::GetInstrumentManager() {
2231            return &instruments;
2232        }
2233    
2234      // static constant initializers      // static constant initializers
2235      const float* Engine::VolumeCurve(InitVolumeCurve());      const Engine::FloatTable Engine::VolumeCurve(InitVolumeCurve());
2236      const float* Engine::PanCurve(InitPanCurve());      const Engine::FloatTable Engine::PanCurve(InitPanCurve());
2237        const Engine::FloatTable Engine::CrossfadeCurve(InitCrossfadeCurve());
2238    
2239      float* Engine::InitVolumeCurve() {      float* Engine::InitVolumeCurve() {
2240          // line-segment approximation          // line-segment approximation
# Line 1584  namespace LinuxSampler { namespace gig { Line 2255  namespace LinuxSampler { namespace gig {
2255          return InitCurve(segments, 129);          return InitCurve(segments, 129);
2256      }      }
2257    
2258        float* Engine::InitCrossfadeCurve() {
2259            // line-segment approximation
2260            const float segments[] = {
2261                0, 0, 1, 0.03, 10, 0.1, 51, 0.58, 127, 1
2262            };
2263            return InitCurve(segments);
2264        }
2265    
2266      float* Engine::InitCurve(const float* segments, int size) {      float* Engine::InitCurve(const float* segments, int size) {
2267          float* y = new float[size];          float* y = new float[size];
2268          for (int x = 0 ; x < size ; x++) {          for (int x = 0 ; x < size ; x++) {

Legend:
Removed from v.831  
changed lines
  Added in v.1933

  ViewVC Help
Powered by ViewVC