/[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 1043 by schoenebeck, Wed Feb 7 21:02:04 2007 UTC revision 1843 by iliev, Sat Feb 21 17:08:18 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-2007 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 72  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 98  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,false>(CONFIG_SYSEX_BUFFER_SIZE, 0);          pSysexBuffer       = new RingBuffer<uint8_t,false>(CONFIG_SYSEX_BUFFER_SIZE, 0);
108          pEventQueue        = new RingBuffer<Event,false>(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          pDimRegionsInUse   = new ::gig::DimensionRegion*[CONFIG_MAX_VOICES + 1];          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          InstrumentChangeQueue      = new RingBuffer<instrument_change_command_t,false>(1, 0);          iMaxDiskStreams    = GLOBAL_MAX_STREAMS;
         InstrumentChangeReplyQueue = new RingBuffer<instrument_change_reply_t,false>(1, 0);  
116    
117          for (RTList<Voice>::Iterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) {          for (RTList<Voice>::Iterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) {
118              iterVoice->SetEngine(this);              iterVoice->SetEngine(this);
# Line 119  namespace LinuxSampler { namespace gig { Line 121  namespace LinuxSampler { namespace gig {
121    
122          ResetInternal();          ResetInternal();
123          ResetScaleTuning();          ResetScaleTuning();
124            ResetSuspendedRegions();
125      }      }
126    
127      /**      /**
# Line 141  namespace LinuxSampler { namespace gig { Line 144  namespace LinuxSampler { namespace gig {
144          if (pEventGenerator) delete pEventGenerator;          if (pEventGenerator) delete pEventGenerator;
145          if (pVoiceStealingQueue) delete pVoiceStealingQueue;          if (pVoiceStealingQueue) delete pVoiceStealingQueue;
146          if (pSysexBuffer) delete pSysexBuffer;          if (pSysexBuffer) delete pSysexBuffer;
147            if (pGlobalEvents) delete pGlobalEvents;
148            if (pDimRegionPool[0]) delete pDimRegionPool[0];
149            if (pDimRegionPool[1]) delete pDimRegionPool[1];
150            ResetSuspendedRegions();
151          Unregister();          Unregister();
152      }      }
153    
# Line 150  namespace LinuxSampler { namespace gig { Line 157  namespace LinuxSampler { namespace gig {
157          dmsg(3,("gig::Engine: enabled (val=%d)\n", EngineDisabled.GetUnsafe()));          dmsg(3,("gig::Engine: enabled (val=%d)\n", EngineDisabled.GetUnsafe()));
158      }      }
159    
160        /**
161         * Temporarily stop the engine to not do anything. The engine will just be
162         * frozen during that time, that means after enabling it again it will
163         * continue where it was, with all its voices and playback state it had at
164         * the point of disabling. Notice that the engine's (audio) thread will
165         * continue to run, it just remains in an inactive loop during that time.
166         *
167         * If you need to be sure that all voices and disk streams are killed as
168         * well, use @c SuspendAll() instead.
169         *
170         * @see Enable(), SuspendAll()
171         */
172      void Engine::Disable() {      void Engine::Disable() {
173          dmsg(3,("gig::Engine: disabling\n"));          dmsg(3,("gig::Engine: disabling\n"));
174          bool* pWasDisabled = EngineDisabled.PushAndUnlock(true, 2); // wait max. 2s          bool* pWasDisabled = EngineDisabled.PushAndUnlock(true, 2); // wait max. 2s
# Line 163  namespace LinuxSampler { namespace gig { Line 182  namespace LinuxSampler { namespace gig {
182      }      }
183    
184      /**      /**
185         * Similar to @c Disable() but this method additionally kills all voices
186         * and disk streams and blocks until all voices and disk streams are actually
187         * killed / deleted.
188         *
189         * @e Note: only the original calling thread is able to re-enable the
190         * engine afterwards by calling @c ResumeAll() later on!
191         */
192        void Engine::SuspendAll() {
193            dmsg(2,("gig::Engine: Suspending all ...\n"));
194            // stop the engine, so we can safely modify the engine's
195            // data structures from this foreign thread
196            DisableAndLock();
197            // we could also use the respective class member variable here,
198            // but this is probably safer and cleaner
199            int iPendingStreamDeletions = 0;
200            // kill all voices on all engine channels the *die hard* way
201            for (int iChannel = 0; iChannel < engineChannels.size(); iChannel++) {
202                EngineChannel* pEngineChannel = engineChannels[iChannel];
203                RTList<uint>::Iterator iuiKey = pEngineChannel->pActiveKeys->first();
204                RTList<uint>::Iterator end    = pEngineChannel->pActiveKeys->end();
205                for (; iuiKey != end; ++iuiKey) { // iterate through all active keys
206                    midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[*iuiKey];
207                    RTList<Voice>::Iterator itVoice = pKey->pActiveVoices->first();
208                    RTList<Voice>::Iterator itVoicesEnd = pKey->pActiveVoices->end();
209                    for (; itVoice != itVoicesEnd; ++itVoice) { // iterate through all voices on this key
210                        // request a notification from disk thread side for stream deletion
211                        const Stream::Handle hStream = itVoice->KillImmediately(true);
212                        if (hStream != Stream::INVALID_HANDLE) { // voice actually used a stream
213                            iPendingStreamDeletions++;
214                        }
215                    }
216                }
217            }
218            // wait until all streams were actually deleted by the disk thread
219            while (iPendingStreamDeletions) {
220                while (
221                    iPendingStreamDeletions &&
222                    pDiskThread->AskForDeletedStream() != Stream::INVALID_HANDLE
223                ) iPendingStreamDeletions--;
224                if (!iPendingStreamDeletions) break;
225                usleep(10000); // sleep for 10ms
226            }
227            dmsg(2,("gig::Engine: Everything suspended.\n"));
228        }
229    
230        /**
231         * At the moment same as calling @c Enable() directly, but this might
232         * change in future, so better call this method as counterpart to
233         * @c SuspendAll() instead of @c Enable() !
234         */
235        void Engine::ResumeAll() {
236            Enable();
237        }
238    
239        /**
240         * Order the engine to stop rendering audio for the given region.
241         * Additionally this method will block until all voices and their disk
242         * streams associated with that region are actually killed / deleted, so
243         * one can i.e. safely modify the region with an instrument editor after
244         * returning from this method.
245         *
246         * @param pRegion - region the engine shall stop using
247         */
248        void Engine::Suspend(::gig::Region* pRegion) {
249            dmsg(2,("gig::Engine: Suspending Region %x ...\n",pRegion));
250            SuspendedRegionsMutex.Lock();
251            SuspensionChangeOngoing.Set(true);
252            pPendingRegionSuspension = pRegion;
253            SuspensionChangeOngoing.WaitAndUnlockIf(true);
254            SuspendedRegionsMutex.Unlock();
255            dmsg(2,("gig::Engine: Region %x suspended.",pRegion));
256        }
257    
258        /**
259         * Orders the engine to resume playing back the given region, previously
260         * suspended with @c Suspend() .
261         *
262         * @param pRegion - region the engine shall be allowed to use again
263         */
264        void Engine::Resume(::gig::Region* pRegion) {
265            dmsg(2,("gig::Engine: Resuming Region %x ...\n",pRegion));
266            SuspendedRegionsMutex.Lock();
267            SuspensionChangeOngoing.Set(true);
268            pPendingRegionResumption = pRegion;
269            SuspensionChangeOngoing.WaitAndUnlockIf(true);
270            SuspendedRegionsMutex.Unlock();
271            dmsg(2,("gig::Engine: Region %x resumed.\n",pRegion));
272        }
273    
274        /**
275       *  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
276       *  control and status variables.       *  control and status variables.
277       */       */
# Line 183  namespace LinuxSampler { namespace gig { Line 292  namespace LinuxSampler { namespace gig {
292          // make sure that the engine does not get any sysex messages          // make sure that the engine does not get any sysex messages
293          // while it's reseting          // while it's reseting
294          bool sysexDisabled = MidiInputPort::RemoveSysexListener(this);          bool sysexDisabled = MidiInputPort::RemoveSysexListener(this);
295          ActiveVoiceCount    = 0;          SetVoiceCount(0);
296          ActiveVoiceCountMax = 0;          ActiveVoiceCountMax = 0;
297    
298          // reset voice stealing parameters          // reset voice stealing parameters
# Line 217  namespace LinuxSampler { namespace gig { Line 326  namespace LinuxSampler { namespace gig {
326          memset(&ScaleTuning[0], 0x00, 12);          memset(&ScaleTuning[0], 0x00, 12);
327      }      }
328    
329        void Engine::ResetSuspendedRegions() {
330            SuspendedRegions.clear();
331            iPendingStreamDeletions = 0;
332            pPendingRegionSuspension = pPendingRegionResumption = NULL;
333            SuspensionChangeOngoing.Set(false);
334        }
335    
336      /**      /**
337       * Connect this engine instance with the given audio output device.       * Connect this engine instance with the given audio output device.
338       * This method will be called when an Engine instance is created.       * This method will be called when an Engine instance is created.
# Line 227  namespace LinuxSampler { namespace gig { Line 343  namespace LinuxSampler { namespace gig {
343       * @param pAudioOut - audio output device to connect to       * @param pAudioOut - audio output device to connect to
344       */       */
345      void Engine::Connect(AudioOutputDevice* pAudioOut) {      void Engine::Connect(AudioOutputDevice* pAudioOut) {
346            // caution: don't ignore if connecting to the same device here,
347            // because otherwise SetMaxDiskStreams() implementation won't work anymore!
348    
349          pAudioOutputDevice = pAudioOut;          pAudioOutputDevice = pAudioOut;
350    
351          ResetInternal();          ResetInternal();
# Line 243  namespace LinuxSampler { namespace gig { Line 362  namespace LinuxSampler { namespace gig {
362          this->MaxSamplesPerCycle = pAudioOutputDevice->MaxSamplesPerCycle();          this->MaxSamplesPerCycle = pAudioOutputDevice->MaxSamplesPerCycle();
363          this->SampleRate         = pAudioOutputDevice->SampleRate();          this->SampleRate         = pAudioOutputDevice->SampleRate();
364    
365          // FIXME: audio drivers with varying fragment sizes might be a problem here          MinFadeOutSamples = int(double(SampleRate) * CONFIG_EG_MIN_RELEASE_TIME) - 1;
366          MaxFadeOutPos = MaxSamplesPerCycle - int(double(SampleRate) * CONFIG_EG_MIN_RELEASE_TIME) - 1;          if (MaxSamplesPerCycle < MinFadeOutSamples) {
         if (MaxFadeOutPos < 0) {  
367              std::cerr << "gig::Engine: WARNING, CONFIG_EG_MIN_RELEASE_TIME "              std::cerr << "gig::Engine: WARNING, CONFIG_EG_MIN_RELEASE_TIME "
368                        << "too big for current audio fragment size & sampling rate! "                        << "too big for current audio fragment size & sampling rate! "
369                        << "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;
370              // force volume ramp downs at the beginning of each fragment              // force volume ramp downs at the beginning of each fragment
371              MaxFadeOutPos = 0;              MinFadeOutSamples = MaxSamplesPerCycle;
372              // lower minimum release time              // lower minimum release time
373              const float minReleaseTime = (float) MaxSamplesPerCycle / (float) SampleRate;              const float minReleaseTime = (float) MaxSamplesPerCycle / (float) SampleRate;
374              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 266  namespace LinuxSampler { namespace gig { Line 384  namespace LinuxSampler { namespace gig {
384              delete this->pDiskThread;              delete this->pDiskThread;
385              dmsg(1,("OK\n"));              dmsg(1,("OK\n"));
386          }          }
387          this->pDiskThread = new DiskThread(((pAudioOut->MaxSamplesPerCycle() << CONFIG_MAX_PITCH) << 1) + 6, //FIXME: assuming stereo          this->pDiskThread =
388                                             &instruments);              new DiskThread(
389                    iMaxDiskStreams,
390                    ((pAudioOut->MaxSamplesPerCycle() << CONFIG_MAX_PITCH) << 1) + 6, //FIXME: assuming stereo
391                    &instruments
392                );
393    
394          if (!pDiskThread) {          if (!pDiskThread) {
395              dmsg(0,("gig::Engine  new diskthread = NULL\n"));              dmsg(0,("gig::Engine  new diskthread = NULL\n"));
396              exit(EXIT_FAILURE);              exit(EXIT_FAILURE);
# Line 293  namespace LinuxSampler { namespace gig { Line 416  namespace LinuxSampler { namespace gig {
416                  exit(EXIT_FAILURE);                  exit(EXIT_FAILURE);
417              }              }
418          }          }
419            pVoicePool->clear();
420        }
421    
422        /**
423         * Called by the engine's (audio) thread once per cycle to process requests
424         * from the outer world to suspend or resume a given @c gig::Region .
425         */
426        void Engine::ProcessSuspensionsChanges() {
427            // process request for suspending one region
428            if (pPendingRegionSuspension) {
429                // kill all voices on all engine channels that use this region
430                for (int iChannel = 0; iChannel < engineChannels.size(); iChannel++) {
431                    EngineChannel* pEngineChannel = engineChannels[iChannel];
432                    RTList<uint>::Iterator iuiKey = pEngineChannel->pActiveKeys->first();
433                    RTList<uint>::Iterator end    = pEngineChannel->pActiveKeys->end();
434                    for (; iuiKey != end; ++iuiKey) { // iterate through all active keys
435                        midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[*iuiKey];
436                        RTList<Voice>::Iterator itVoice = pKey->pActiveVoices->first();
437                        // if current key is not associated with this region, skip this key
438                        if (itVoice->pDimRgn->GetParent() != pPendingRegionSuspension) continue;
439                        RTList<Voice>::Iterator itVoicesEnd = pKey->pActiveVoices->end();
440                        for (; itVoice != itVoicesEnd; ++itVoice) { // iterate through all voices on this key
441                            // request a notification from disk thread side for stream deletion
442                            const Stream::Handle hStream = itVoice->KillImmediately(true);
443                            if (hStream != Stream::INVALID_HANDLE) { // voice actually used a stream
444                                iPendingStreamDeletions++;
445                            }
446                        }
447                    }
448                }
449                // make sure the region is not yet on the list
450                bool bAlreadySuspended = false;
451                RTList< ::gig::Region*>::Iterator iter = SuspendedRegions.first();
452                RTList< ::gig::Region*>::Iterator end  = SuspendedRegions.end();
453                for (; iter != end; ++iter) { // iterate through all suspended regions
454                    if (*iter == pPendingRegionSuspension) { // found
455                        bAlreadySuspended = true;
456                        dmsg(1,("gig::Engine: attempt to suspend an already suspended region !!!\n"));
457                        break;
458                    }
459                }
460                if (!bAlreadySuspended) {
461                    // put the region on the list of suspended regions
462                    RTList< ::gig::Region*>::Iterator iter = SuspendedRegions.allocAppend();
463                    if (iter) {
464                        *iter = pPendingRegionSuspension;
465                    } else std::cerr << "gig::Engine: Could not suspend Region, list is full. This is a bug!!!\n" << std::flush;
466                }
467                // free request slot for next caller (and to make sure that
468                // we're not going to process the same request in the next cycle)
469                pPendingRegionSuspension = NULL;
470                // if no disk stream deletions are pending, awaken other side, as
471                // we're done in this case
472                if (!iPendingStreamDeletions) SuspensionChangeOngoing.Set(false);
473            }
474    
475            // process request for resuming one region
476            if (pPendingRegionResumption) {
477                // remove region from the list of suspended regions
478                RTList< ::gig::Region*>::Iterator iter = SuspendedRegions.first();
479                RTList< ::gig::Region*>::Iterator end  = SuspendedRegions.end();
480                for (; iter != end; ++iter) { // iterate through all suspended regions
481                    if (*iter == pPendingRegionResumption) { // found
482                        SuspendedRegions.free(iter);
483                        break; // done
484                    }
485                }
486                // free request slot for next caller
487                pPendingRegionResumption = NULL;
488                // awake other side as we're done
489                SuspensionChangeOngoing.Set(false);
490            }
491        }
492    
493        /**
494         * Called by the engine's (audio) thread once per cycle to check if
495         * streams of voices that were killed due to suspension request have
496         * finally really been deleted by the disk thread.
497         */
498        void Engine::ProcessPendingStreamDeletions() {
499            if (!iPendingStreamDeletions) return;
500            //TODO: or shall we better store a list with stream handles instead of a scalar amount of streams to be deleted? might be safer
501            while (
502                iPendingStreamDeletions &&
503                pDiskThread->AskForDeletedStream() != Stream::INVALID_HANDLE
504            ) iPendingStreamDeletions--;
505            // just for safety ...
506            while (pDiskThread->AskForDeletedStream() != Stream::INVALID_HANDLE);
507            // now that all disk streams are deleted, awake other side as
508            // we're finally done with suspending the requested region
509            if (!iPendingStreamDeletions) SuspensionChangeOngoing.Set(false);
510        }
511    
512        /**
513         * Returns @c true if the given region is currently set to be suspended
514         * from being used, @c false otherwise.
515         */
516        bool Engine::RegionSuspended(::gig::Region* pRegion) {
517            if (SuspendedRegions.isEmpty()) return false;
518            //TODO: or shall we use a sorted container instead of the RTList? might be faster ... or trivial ;-)
519            RTList< ::gig::Region*>::Iterator iter = SuspendedRegions.first();
520            RTList< ::gig::Region*>::Iterator end  = SuspendedRegions.end();
521            for (; iter != end; ++iter)  // iterate through all suspended regions
522                if (*iter == pRegion) return true;
523            return false;
524      }      }
525    
526      /**      /**
# Line 350  namespace LinuxSampler { namespace gig { Line 578  namespace LinuxSampler { namespace gig {
578       *  @returns       0 on success       *  @returns       0 on success
579       */       */
580      int Engine::RenderAudio(uint Samples) {      int Engine::RenderAudio(uint Samples) {
581          dmsg(7,("RenderAudio(Samples=%d)\n", Samples));          dmsg(8,("RenderAudio(Samples=%d)\n", Samples));
582    
583          // return if engine disabled          // return if engine disabled
584          if (EngineDisabled.Pop()) {          if (EngineDisabled.Pop()) {
585              dmsg(5,("gig::Engine: engine disabled (val=%d)\n",EngineDisabled.GetUnsafe()));              dmsg(5,("gig::Engine: engine disabled (val=%d)\n",EngineDisabled.GetUnsafe()));
586                EngineDisabled.RttDone();
587              return 0;              return 0;
588          }          }
589    
590            // process requests for suspending / resuming regions (i.e. to avoid
591            // crashes while these regions are modified by an instrument editor)
592            ProcessSuspensionsChanges();
593    
594          // 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)
595          pEventGenerator->UpdateFragmentTime(Samples);          pEventGenerator->UpdateFragmentTime(Samples);
596    
597          // 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
598          // in each audio fragment. All subsequent request for spawning new          // in each audio fragment. All subsequent request for spawning new
599          // voices in the same audio fragment will be ignored.          // voices in the same audio fragment will be ignored.
600          VoiceSpawnsLeft = CONFIG_MAX_VOICES;          VoiceSpawnsLeft = MaxVoices();
601    
602          // 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
603          // (these are usually just SysEx messages)          // (these are usually just SysEx messages)
# Line 388  namespace LinuxSampler { namespace gig { Line 621  namespace LinuxSampler { namespace gig {
621          ActiveVoiceCountTemp = 0;          ActiveVoiceCountTemp = 0;
622    
623          // handle instrument change commands          // handle instrument change commands
624          instrument_change_command_t command;          bool instrumentChanged = false;
625          if (InstrumentChangeQueue->pop(&command) > 0) {          for (int i = 0; i < engineChannels.size(); i++) {
626              EngineChannel* pEngineChannel = command.pEngineChannel;              EngineChannel* pEngineChannel = engineChannels[i];
             pEngineChannel->pInstrument = command.pInstrument;  
   
             // iterate through all active voices and mark their  
             // dimension regions as "in use". The instrument resource  
             // manager may delete all of the instrument except the  
             // dimension regions and samples that are in use.  
             int i = 0;  
             RTList<uint>::Iterator iuiKey = pEngineChannel->pActiveKeys->first();  
             RTList<uint>::Iterator end    = pEngineChannel->pActiveKeys->end();  
             while (iuiKey != end) { // iterate through all active keys  
                 midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[*iuiKey];  
                 ++iuiKey;  
627    
628                  RTList<Voice>::Iterator itVoice     = pKey->pActiveVoices->first();              // as we're going to (carefully) write some status to the
629                  RTList<Voice>::Iterator itVoicesEnd = pKey->pActiveVoices->end();              // synchronized struct, we cast away the const
630                  for (; itVoice != itVoicesEnd; ++itVoice) { // iterate through all voices on this key              EngineChannel::instrument_change_command_t& cmd =
631                      if (!itVoice->Orphan) {                  const_cast<EngineChannel::instrument_change_command_t&>(pEngineChannel->InstrumentChangeCommandReader.Lock());
632    
633                pEngineChannel->pDimRegionsInUse = cmd.pDimRegionsInUse;
634                pEngineChannel->pDimRegionsInUse->clear();
635    
636                if (cmd.bChangeInstrument) {
637                    // change instrument
638                    dmsg(5,("Engine: instrument change command received\n"));
639                    cmd.bChangeInstrument = false;
640                    pEngineChannel->pInstrument = cmd.pInstrument;
641                    instrumentChanged = true;
642    
643                    // Iterate through all active voices and mark them as
644                    // "orphans", which means that the dimension regions
645                    // and samples they use should be released to the
646                    // instrument resource manager when the voices die.
647                    int i = 0;
648                    RTList<uint>::Iterator iuiKey = pEngineChannel->pActiveKeys->first();
649                    RTList<uint>::Iterator end    = pEngineChannel->pActiveKeys->end();
650                    while (iuiKey != end) { // iterate through all active keys
651                        midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[*iuiKey];
652                        ++iuiKey;
653    
654                        RTList<Voice>::Iterator itVoice     = pKey->pActiveVoices->first();
655                        RTList<Voice>::Iterator itVoicesEnd = pKey->pActiveVoices->end();
656                        for (; itVoice != itVoicesEnd; ++itVoice) { // iterate through all voices on this key
657                          itVoice->Orphan = true;                          itVoice->Orphan = true;
                         pDimRegionsInUse[i++] = itVoice->pDimRgn;  
658                      }                      }
659                  }                  }
660              }              }
661              pDimRegionsInUse[i] = 0; // end of list          }
662            if (instrumentChanged) {
663              // send a reply to the calling thread, which is waiting              //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
664              instrument_change_reply_t reply;              ResetSuspendedRegions();
             InstrumentChangeReplyQueue->push(&reply);  
665          }          }
666    
667          // handle events on all engine channels          // handle events on all engine channels
# Line 452  namespace LinuxSampler { namespace gig { Line 696  namespace LinuxSampler { namespace gig {
696          pVoiceStealingQueue->clear();          pVoiceStealingQueue->clear();
697    
698          // just some statistics about this engine instance          // just some statistics about this engine instance
699          ActiveVoiceCount = ActiveVoiceCountTemp;          SetVoiceCount(ActiveVoiceCountTemp);
700          if (ActiveVoiceCount > ActiveVoiceCountMax) ActiveVoiceCountMax = ActiveVoiceCount;          if (VoiceCount() > ActiveVoiceCountMax) ActiveVoiceCountMax = VoiceCount();
701    
702            // in case regions were previously suspended and we killed voices
703            // with disk streams due to that, check if those streams have finally
704            // been deleted by the disk thread
705            if (iPendingStreamDeletions) ProcessPendingStreamDeletions();
706    
707            for (int i = 0; i < engineChannels.size(); i++) {
708                engineChannels[i]->InstrumentChangeCommandReader.Unlock();
709            }
710          FrameTime += Samples;          FrameTime += Samples;
711    
712            EngineDisabled.RttDone();
713          return 0;          return 0;
714      }      }
715    
# Line 522  namespace LinuxSampler { namespace gig { Line 775  namespace LinuxSampler { namespace gig {
775          if (pEngineChannel->GetMute()) return; // skip if sampler channel is muted          if (pEngineChannel->GetMute()) return; // skip if sampler channel is muted
776          #endif          #endif
777    
778            uint voiceCount = 0;
779            uint streamCount = 0;
780          RTList<uint>::Iterator iuiKey = pEngineChannel->pActiveKeys->first();          RTList<uint>::Iterator iuiKey = pEngineChannel->pActiveKeys->first();
781          RTList<uint>::Iterator end    = pEngineChannel->pActiveKeys->end();          RTList<uint>::Iterator end    = pEngineChannel->pActiveKeys->end();
782          while (iuiKey != end) { // iterate through all active keys          while (iuiKey != end) { // iterate through all active keys
# Line 533  namespace LinuxSampler { namespace gig { Line 788  namespace LinuxSampler { namespace gig {
788              for (; itVoice != itVoicesEnd; ++itVoice) { // iterate through all voices on this key              for (; itVoice != itVoicesEnd; ++itVoice) { // iterate through all voices on this key
789                  // now render current voice                  // now render current voice
790                  itVoice->Render(Samples);                  itVoice->Render(Samples);
791                  if (itVoice->IsActive()) ActiveVoiceCountTemp++; // still active                  if (itVoice->IsActive()) { // still active
792                  else { // voice reached end, is now inactive                      if (!itVoice->Orphan) {
793                            *(pEngineChannel->pDimRegionsInUse->allocAppend()) = itVoice->pDimRgn;
794                        }
795                        ActiveVoiceCountTemp++;
796                        voiceCount++;
797    
798                        if (itVoice->PlaybackState == Voice::playback_state_disk) {
799                            if ((itVoice->DiskStreamRef).State != Stream::state_unused) streamCount++;
800                        }
801                    }  else { // voice reached end, is now inactive
802                      FreeVoice(pEngineChannel, itVoice); // remove voice from the list of active voices                      FreeVoice(pEngineChannel, itVoice); // remove voice from the list of active voices
803                  }                  }
804              }              }
805          }          }
806    
807            pEngineChannel->SetVoiceCount(voiceCount);
808            pEngineChannel->SetDiskStreamCount(streamCount);
809      }      }
810    
811      /**      /**
# Line 564  namespace LinuxSampler { namespace gig { Line 831  namespace LinuxSampler { namespace gig {
831                  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);
832              if (itNewVoice) {              if (itNewVoice) {
833                  itNewVoice->Render(Samples);                  itNewVoice->Render(Samples);
834                  if (itNewVoice->IsActive()) ActiveVoiceCountTemp++; // still active                  if (itNewVoice->IsActive()) { // still active
835                  else { // voice reached end, is now inactive                      *(pEngineChannel->pDimRegionsInUse->allocAppend()) = itNewVoice->pDimRgn;
836                        ActiveVoiceCountTemp++;
837                        pEngineChannel->SetVoiceCount(pEngineChannel->GetVoiceCount() + 1);
838    
839                        if (itNewVoice->PlaybackState == Voice::playback_state_disk) {
840                            if (itNewVoice->DiskStreamRef.State != Stream::state_unused) {
841                                pEngineChannel->SetDiskStreamCount(pEngineChannel->GetDiskStreamCount() + 1);
842                            }
843                        }
844                    } else { // voice reached end, is now inactive
845                      FreeVoice(pEngineChannel, itNewVoice); // remove voice from the list of active voices                      FreeVoice(pEngineChannel, itNewVoice); // remove voice from the list of active voices
846                  }                  }
847              }              }
# Line 592  namespace LinuxSampler { namespace gig { Line 868  namespace LinuxSampler { namespace gig {
868       *                         this audio fragment cycle       *                         this audio fragment cycle
869       */       */
870      void Engine::RouteAudio(EngineChannel* pEngineChannel, uint Samples) {      void Engine::RouteAudio(EngineChannel* pEngineChannel, uint Samples) {
871          // route master signal          // route dry signal
872          {          {
873              AudioChannel* pDstL = pAudioOutputDevice->Channel(pEngineChannel->AudioDeviceChannelLeft);              AudioChannel* pDstL = pAudioOutputDevice->Channel(pEngineChannel->AudioDeviceChannelLeft);
874              AudioChannel* pDstR = pAudioOutputDevice->Channel(pEngineChannel->AudioDeviceChannelRight);              AudioChannel* pDstR = pAudioOutputDevice->Channel(pEngineChannel->AudioDeviceChannelRight);
# Line 603  namespace LinuxSampler { namespace gig { Line 879  namespace LinuxSampler { namespace gig {
879          {          {
880              for (int iFxSend = 0; iFxSend < pEngineChannel->GetFxSendCount(); iFxSend++) {              for (int iFxSend = 0; iFxSend < pEngineChannel->GetFxSendCount(); iFxSend++) {
881                  FxSend* pFxSend = pEngineChannel->GetFxSend(iFxSend);                  FxSend* pFxSend = pEngineChannel->GetFxSend(iFxSend);
882                  // left channel                  for (int iChan = 0; iChan < 2; ++iChan) {
883                  const int iDstL = pFxSend->DestinationChannel(0);                      AudioChannel* pSource =
884                  if (iDstL < 0) {                          (iChan)
885                      dmsg(1,("Engine::RouteAudio() Error: invalid FX send (L) destination channel"));                              ? pEngineChannel->pChannelRight
886                  } else {                              : pEngineChannel->pChannelLeft;
887                      AudioChannel* pDstL = pAudioOutputDevice->Channel(iDstL);                      const int iDstChan = pFxSend->DestinationChannel(iChan);
888                      if (!pDstL) {                      if (iDstChan < 0) {
889                          dmsg(1,("Engine::RouteAudio() Error: invalid FX send (L) destination channel"));                          dmsg(1,("Engine::RouteAudio() Error: invalid FX send (%s) destination channel (%d->%d)", ((iChan) ? "R" : "L"), iChan, iDstChan));
890                      } else pEngineChannel->pChannelLeft->MixTo(pDstL, Samples, pFxSend->Level());                          goto channel_cleanup;
891                  }                      }
892                  // right channel                      AudioChannel* pDstChan = NULL;
893                  const int iDstR = pFxSend->DestinationChannel(1);                      if (pFxSend->DestinationMasterEffectChain() >= 0) { // fx send routed to an internal master effect
894                  if (iDstR < 0) {                          EffectChain* pEffectChain =
895                      dmsg(1,("Engine::RouteAudio() Error: invalid FX send (R) destination channel"));                              pAudioOutputDevice->MasterEffectChain(
896                  } else {                                  pFxSend->DestinationMasterEffectChain()
897                      AudioChannel* pDstR = pAudioOutputDevice->Channel(iDstR);                              );
898                      if (!pDstR) {                          if (!pEffectChain) {
899                          dmsg(1,("Engine::RouteAudio() Error: invalid FX send (R) destination channel"));                              dmsg(1,("Engine::RouteAudio() Error: invalid FX send (%s) destination effect chain %d", ((iChan) ? "R" : "L"), pFxSend->DestinationMasterEffectChain()));
900                      } else pEngineChannel->pChannelRight->MixTo(pDstR, Samples, pFxSend->Level());                              goto channel_cleanup;
901                            }
902                            Effect* pEffect =
903                                pEffectChain->GetEffect(
904                                    pFxSend->DestinationMasterEffect()
905                                );
906                            if (!pEffect) {
907                                dmsg(1,("Engine::RouteAudio() Error: invalid FX send (%s) destination effect %d of effect chain %d", ((iChan) ? "R" : "L"), pFxSend->DestinationMasterEffect(), pFxSend->DestinationMasterEffectChain()));
908                                goto channel_cleanup;
909                            }
910                            pDstChan = pEffect->InputChannel(iDstChan);
911                        } else { // FX send routed directly to an audio output channel
912                            pDstChan = pAudioOutputDevice->Channel(iDstChan);
913                        }
914                        if (!pDstChan) {
915                            dmsg(1,("Engine::RouteAudio() Error: invalid FX send (%s) destination channel (%d->%d)", ((iChan) ? "R" : "L"), iChan, iDstChan));
916                            goto channel_cleanup;
917                        }
918                        pSource->MixTo(pDstChan, Samples, pFxSend->Level());
919                  }                  }
920              }              }
921          }          }
922            channel_cleanup:
923          // reset buffers with silence (zero out) for the next audio cycle          // reset buffers with silence (zero out) for the next audio cycle
924          pEngineChannel->pChannelLeft->Clear();          pEngineChannel->pChannelLeft->Clear();
925          pEngineChannel->pChannelRight->Clear();          pEngineChannel->pChannelRight->Clear();
# Line 670  namespace LinuxSampler { namespace gig { Line 965  namespace LinuxSampler { namespace gig {
965       *       *
966       *  @param pData - pointer to sysex data       *  @param pData - pointer to sysex data
967       *  @param Size  - lenght of sysex data (in bytes)       *  @param Size  - lenght of sysex data (in bytes)
968         *  @param pSender - the MIDI input port on which the SysEx message was
969         *                   received
970       */       */
971      void Engine::SendSysex(void* pData, uint Size) {      void Engine::SendSysex(void* pData, uint Size, MidiInputPort* pSender) {
972          Event event             = pEventGenerator->CreateEvent();          Event event             = pEventGenerator->CreateEvent();
973          event.Type              = Event::type_sysex;          event.Type              = Event::type_sysex;
974          event.Param.Sysex.Size  = Size;          event.Param.Sysex.Size  = Size;
975          event.pEngineChannel    = NULL; // as Engine global event          event.pEngineChannel    = NULL; // as Engine global event
976            event.pMidiInputPort    = pSender;
977          if (pEventQueue->write_space() > 0) {          if (pEventQueue->write_space() > 0) {
978              if (pSysexBuffer->write_space() >= Size) {              if (pSysexBuffer->write_space() >= Size) {
979                  // copy sysex data to input buffer                  // copy sysex data to input buffer
# Line 769  namespace LinuxSampler { namespace gig { Line 1067  namespace LinuxSampler { namespace gig {
1067          {          {
1068              // first, get total amount of required voices (dependant on amount of layers)              // first, get total amount of required voices (dependant on amount of layers)
1069              ::gig::Region* pRegion = pEngineChannel->pInstrument->GetRegion(itNoteOnEventOnKeyList->Param.Note.Key);              ::gig::Region* pRegion = pEngineChannel->pInstrument->GetRegion(itNoteOnEventOnKeyList->Param.Note.Key);
1070              if (pRegion) {              if (pRegion && !RegionSuspended(pRegion)) {
1071                  int voicesRequired = pRegion->Layers;                  int voicesRequired = pRegion->Layers;
1072                  // now launch the required amount of voices                  // now launch the required amount of voices
1073                  for (int i = 0; i < voicesRequired; i++)                  for (int i = 0; i < voicesRequired; i++)
# Line 1353  namespace LinuxSampler { namespace gig { Line 1651  namespace LinuxSampler { namespace gig {
1651      void Engine::ProcessControlChange(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itControlChangeEvent) {      void Engine::ProcessControlChange(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itControlChangeEvent) {
1652          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));
1653    
1654            // handle the "control triggered" MIDI rule: a control change
1655            // event can trigger a new note on or note off event
1656            if (pEngineChannel->pInstrument) {
1657    
1658                ::gig::MidiRule* rule;
1659                for (int i = 0 ; (rule = pEngineChannel->pInstrument->GetMidiRule(i)) ; i++) {
1660    
1661                    if (::gig::MidiRuleCtrlTrigger* ctrlTrigger =
1662                        dynamic_cast< ::gig::MidiRuleCtrlTrigger*>(rule)) {
1663                        if (itControlChangeEvent->Param.CC.Controller ==
1664                            ctrlTrigger->ControllerNumber) {
1665    
1666                            uint8_t oldCCValue = pEngineChannel->ControllerTable[
1667                                itControlChangeEvent->Param.CC.Controller];
1668                            uint8_t newCCValue = itControlChangeEvent->Param.CC.Value;
1669    
1670                            for (int i = 0 ; i < ctrlTrigger->Triggers ; i++) {
1671                                ::gig::MidiRuleCtrlTrigger::trigger_t* pTrigger =
1672                                      &ctrlTrigger->pTriggers[i];
1673    
1674                                // check if the controller has passed the
1675                                // trigger point in the right direction
1676                                if ((pTrigger->Descending &&
1677                                     oldCCValue > pTrigger->TriggerPoint &&
1678                                     newCCValue <= pTrigger->TriggerPoint) ||
1679                                    (!pTrigger->Descending &&
1680                                     oldCCValue < pTrigger->TriggerPoint &&
1681                                     newCCValue >= pTrigger->TriggerPoint)) {
1682    
1683                                    RTList<Event>::Iterator itNewEvent = pGlobalEvents->allocAppend();
1684                                    if (itNewEvent) {
1685                                        *itNewEvent = *itControlChangeEvent;
1686                                        itNewEvent->Param.Note.Key = pTrigger->Key;
1687    
1688                                        if (pTrigger->NoteOff || pTrigger->Velocity == 0) {
1689                                            itNewEvent->Type = Event::type_note_off;
1690                                            itNewEvent->Param.Note.Velocity = 100;
1691    
1692                                            ProcessNoteOff(pEngineChannel, itNewEvent);
1693                                        } else {
1694                                            itNewEvent->Type = Event::type_note_on;
1695                                            //TODO: if Velocity is 255, the triggered velocity should
1696                                            // depend on how fast the controller is moving
1697                                            itNewEvent->Param.Note.Velocity =
1698                                                pTrigger->Velocity == 255 ? 100 :
1699                                                pTrigger->Velocity;
1700    
1701                                            ProcessNoteOn(pEngineChannel, itNewEvent);
1702                                        }
1703                                    }
1704                                    else dmsg(1,("Event pool emtpy!\n"));
1705                                }
1706                            }
1707                        }
1708                    }
1709                }
1710            }
1711    
1712          // update controller value in the engine channel's controller table          // update controller value in the engine channel's controller table
1713          pEngineChannel->ControllerTable[itControlChangeEvent->Param.CC.Controller] = itControlChangeEvent->Param.CC.Value;          pEngineChannel->ControllerTable[itControlChangeEvent->Param.CC.Controller] = itControlChangeEvent->Param.CC.Value;
1714    
# Line 1372  namespace LinuxSampler { namespace gig { Line 1728  namespace LinuxSampler { namespace gig {
1728                      // workaround, so we won't have hanging notes                      // workaround, so we won't have hanging notes
1729                      ReleaseAllVoices(pEngineChannel, itControlChangeEvent);                      ReleaseAllVoices(pEngineChannel, itControlChangeEvent);
1730                  }                  }
1731                    // to avoid other MIDI CC #6 messages to be misenterpreted as RPN controller data
1732                    pEngineChannel->ResetMidiRpnController();
1733                  break;                  break;
1734              }              }
1735              case 7: { // volume              case 7: { // volume
# Line 1384  namespace LinuxSampler { namespace gig { Line 1742  namespace LinuxSampler { namespace gig {
1742                  //TODO: not sample accurate yet                  //TODO: not sample accurate yet
1743                  pEngineChannel->GlobalPanLeft  = PanCurve[128 - itControlChangeEvent->Param.CC.Value];                  pEngineChannel->GlobalPanLeft  = PanCurve[128 - itControlChangeEvent->Param.CC.Value];
1744                  pEngineChannel->GlobalPanRight = PanCurve[itControlChangeEvent->Param.CC.Value];                  pEngineChannel->GlobalPanRight = PanCurve[itControlChangeEvent->Param.CC.Value];
1745                    pEngineChannel->iLastPanRequest = itControlChangeEvent->Param.CC.Value;
1746                  break;                  break;
1747              }              }
1748              case 64: { // sustain              case 64: { // sustain
# Line 1434  namespace LinuxSampler { namespace gig { Line 1793  namespace LinuxSampler { namespace gig {
1793                  break;                  break;
1794              }              }
1795              case 65: { // portamento on / off              case 65: { // portamento on / off
1796                  KillAllVoices(pEngineChannel, itControlChangeEvent);                  const bool bPortamento = itControlChangeEvent->Param.CC.Value >= 64;
1797                  pEngineChannel->PortamentoMode = itControlChangeEvent->Param.CC.Value >= 64;                  if (bPortamento != pEngineChannel->PortamentoMode)
1798                        KillAllVoices(pEngineChannel, itControlChangeEvent);
1799                    pEngineChannel->PortamentoMode = bPortamento;
1800                  break;                  break;
1801              }              }
1802              case 66: { // sostenuto              case 66: { // sostenuto
# Line 1505  namespace LinuxSampler { namespace gig { Line 1866  namespace LinuxSampler { namespace gig {
1866                  break;                  break;
1867              }              }
1868              case 126: { // mono mode on              case 126: { // mono mode on
1869                  KillAllVoices(pEngineChannel, itControlChangeEvent);                  if (!pEngineChannel->SoloMode)
1870                        KillAllVoices(pEngineChannel, itControlChangeEvent);
1871                  pEngineChannel->SoloMode = true;                  pEngineChannel->SoloMode = true;
1872                  break;                  break;
1873              }              }
1874              case 127: { // poly mode on              case 127: { // poly mode on
1875                  KillAllVoices(pEngineChannel, itControlChangeEvent);                  if (pEngineChannel->SoloMode)
1876                        KillAllVoices(pEngineChannel, itControlChangeEvent);
1877                  pEngineChannel->SoloMode = false;                  pEngineChannel->SoloMode = false;
1878                  break;                  break;
1879              }              }
# Line 1520  namespace LinuxSampler { namespace gig { Line 1883  namespace LinuxSampler { namespace gig {
1883          if (!pEngineChannel->fxSends.empty()) {          if (!pEngineChannel->fxSends.empty()) {
1884              for (int iFxSend = 0; iFxSend < pEngineChannel->GetFxSendCount(); iFxSend++) {              for (int iFxSend = 0; iFxSend < pEngineChannel->GetFxSendCount(); iFxSend++) {
1885                  FxSend* pFxSend = pEngineChannel->GetFxSend(iFxSend);                  FxSend* pFxSend = pEngineChannel->GetFxSend(iFxSend);
1886                  if (pFxSend->MidiController() == itControlChangeEvent->Param.CC.Controller)                  if (pFxSend->MidiController() == itControlChangeEvent->Param.CC.Controller) {
1887                      pFxSend->SetLevel(itControlChangeEvent->Param.CC.Value);                      pFxSend->SetLevel(itControlChangeEvent->Param.CC.Value);
1888                        pFxSend->SetInfoChanged(true);
1889                    }
1890              }              }
1891          }          }
1892      }      }
# Line 1540  namespace LinuxSampler { namespace gig { Line 1905  namespace LinuxSampler { namespace gig {
1905          if (exclusive_status != 0xF0)       goto free_sysex_data;          if (exclusive_status != 0xF0)       goto free_sysex_data;
1906    
1907          switch (id) {          switch (id) {
1908                case 0x7f: { // (Realtime) Universal Sysex (GM Standard)
1909                    uint8_t sysex_channel, sub_id1, sub_id2, val_msb, val_lsb;;
1910                    if (!reader.pop(&sysex_channel)) goto free_sysex_data;
1911                    if (!reader.pop(&sub_id1)) goto free_sysex_data;
1912                    if (!reader.pop(&sub_id2)) goto free_sysex_data;
1913                    if (!reader.pop(&val_lsb)) goto free_sysex_data;
1914                    if (!reader.pop(&val_msb)) goto free_sysex_data;
1915                    //TODO: for now we simply ignore the sysex channel, seldom used anyway
1916                    switch (sub_id1) {
1917                        case 0x04: // Device Control
1918                            switch (sub_id2) {
1919                                case 0x01: { // Master Volume
1920                                    const double volume =
1921                                        double((uint(val_msb)<<7) | uint(val_lsb)) / 16383.0;
1922                                    #if CONFIG_MASTER_VOLUME_SYSEX_BY_PORT
1923                                    // apply volume to all sampler channels that
1924                                    // are connected to the same MIDI input port
1925                                    // this sysex message arrived on
1926                                    for (int i = 0; i < engineChannels.size(); ++i) {
1927                                        EngineChannel* pEngineChannel = engineChannels[i];
1928                                        if (pEngineChannel->GetMidiInputPort() ==
1929                                            itSysexEvent->pMidiInputPort)
1930                                        {
1931                                            pEngineChannel->Volume(volume);
1932                                        }
1933                                    }
1934                                    #else
1935                                    // apply volume globally to the whole sampler
1936                                    GLOBAL_VOLUME = volume;
1937                                    #endif // CONFIG_MASTER_VOLUME_SYSEX_BY_PORT
1938                                    break;
1939                                }
1940                            }
1941                            break;
1942                    }
1943                    break;
1944                }
1945              case 0x41: { // Roland              case 0x41: { // Roland
1946                  dmsg(3,("Roland Sysex\n"));                  dmsg(3,("Roland Sysex\n"));
1947                  uint8_t device_id, model_id, cmd_id;                  uint8_t device_id, model_id, cmd_id;
# Line 1576  namespace LinuxSampler { namespace gig { Line 1978  namespace LinuxSampler { namespace gig {
1978                              dmsg(3,("\t\t\tNew scale applied.\n"));                              dmsg(3,("\t\t\tNew scale applied.\n"));
1979                              break;                              break;
1980                          }                          }
1981                            case 0x15: { // chromatic / drumkit mode
1982                                dmsg(3,("\t\tMIDI Instrument Map Switch\n"));
1983                                uint8_t part = addr[1] & 0x0f;
1984                                uint8_t map;
1985                                if (!reader.pop(&map)) goto free_sysex_data;
1986                                for (int i = 0; i < engineChannels.size(); ++i) {
1987                                    EngineChannel* pEngineChannel = engineChannels[i];
1988                                    if (
1989                                        (pEngineChannel->midiChannel == part ||
1990                                         pEngineChannel->midiChannel == midi_chan_all) &&
1991                                         pEngineChannel->GetMidiInputPort() == itSysexEvent->pMidiInputPort
1992                                    ) {
1993                                        try {
1994                                            pEngineChannel->SetMidiInstrumentMap(map);
1995                                        } catch (Exception e) {
1996                                            dmsg(2,("\t\t\tCould not apply MIDI instrument map %d to part %d: %s\n", map, part, e.Message().c_str()));
1997                                            goto free_sysex_data;
1998                                        } catch (...) {
1999                                            dmsg(2,("\t\t\tCould not apply MIDI instrument map %d to part %d (unknown exception)\n", map, part));
2000                                            goto free_sysex_data;
2001                                        }
2002                                    }
2003                                }
2004                                dmsg(3,("\t\t\tApplied MIDI instrument map %d to part %d.\n", map, part));
2005                                break;
2006                            }
2007                      }                      }
2008                  }                  }
2009                  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 1682  namespace LinuxSampler { namespace gig { Line 2110  namespace LinuxSampler { namespace gig {
2110      }      }
2111    
2112      uint Engine::VoiceCount() {      uint Engine::VoiceCount() {
2113          return ActiveVoiceCount;          return atomic_read(&ActiveVoiceCount);
2114        }
2115    
2116        void Engine::SetVoiceCount(uint Count) {
2117            atomic_set(&ActiveVoiceCount, Count);
2118      }      }
2119    
2120      uint Engine::VoiceCountMax() {      uint Engine::VoiceCountMax() {
2121          return ActiveVoiceCountMax;          return ActiveVoiceCountMax;
2122      }      }
2123    
2124        int Engine::MaxVoices() {
2125            return pVoicePool->poolSize();
2126        }
2127    
2128        void Engine::SetMaxVoices(int iVoices) throw (Exception) {
2129            if (iVoices < 1)
2130                throw Exception("Maximum voices for an engine cannot be set lower than 1");
2131    
2132            SuspendAll();
2133    
2134            // NOTE: we need to clear pDimRegionsInUse before deleting pDimRegionPool,
2135            // otherwise memory corruption will occur if there are active voices (see bug #118)
2136            for (int iChannel = 0; iChannel < engineChannels.size(); iChannel++) {
2137                engineChannels[iChannel]->ClearDimRegionsInUse();
2138            }
2139    
2140            if (pDimRegionPool[0]) delete pDimRegionPool[0];
2141            if (pDimRegionPool[1]) delete pDimRegionPool[1];
2142    
2143            pDimRegionPool[0] = new Pool< ::gig::DimensionRegion*>(iVoices);
2144            pDimRegionPool[1] = new Pool< ::gig::DimensionRegion*>(iVoices);
2145    
2146            for (int iChannel = 0; iChannel < engineChannels.size(); iChannel++) {
2147                engineChannels[iChannel]->ResetDimRegionsInUse();
2148            }
2149    
2150            try {
2151                pVoicePool->resizePool(iVoices);
2152            } catch (...) {
2153                throw Exception("FATAL: Could not resize voice pool!");
2154            }
2155    
2156            for (RTList<Voice>::Iterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) {
2157                iterVoice->SetEngine(this);
2158                iterVoice->pDiskThread = this->pDiskThread;
2159            }
2160            pVoicePool->clear();
2161    
2162            ResumeAll();
2163        }
2164    
2165      bool Engine::DiskStreamSupported() {      bool Engine::DiskStreamSupported() {
2166          return true;          return true;
2167      }      }
2168    
2169      uint Engine::DiskStreamCount() {      uint Engine::DiskStreamCount() {
2170          return (pDiskThread) ? pDiskThread->ActiveStreamCount : 0;          return (pDiskThread) ? pDiskThread->GetActiveStreamCount() : 0;
2171      }      }
2172    
2173      uint Engine::DiskStreamCountMax() {      uint Engine::DiskStreamCountMax() {
2174          return (pDiskThread) ? pDiskThread->ActiveStreamCountMax : 0;          return (pDiskThread) ? pDiskThread->ActiveStreamCountMax : 0;
2175      }      }
2176    
2177        int Engine::MaxDiskStreams() {
2178            return iMaxDiskStreams;
2179        }
2180    
2181        void Engine::SetMaxDiskStreams(int iStreams) throw (Exception) {
2182            if (iStreams < 0)
2183                throw Exception("Maximum disk streams for an engine cannot be set lower than 0");
2184    
2185            SuspendAll();
2186    
2187            iMaxDiskStreams = iStreams;
2188    
2189            // reconnect to audio output device, because that will automatically
2190            // recreate the disk thread with the required amount of streams
2191            if (pAudioOutputDevice) Connect(pAudioOutputDevice);
2192    
2193            ResumeAll();
2194        }
2195    
2196      String Engine::DiskStreamBufferFillBytes() {      String Engine::DiskStreamBufferFillBytes() {
2197          return pDiskThread->GetBufferFillBytes();          return pDiskThread->GetBufferFillBytes();
2198      }      }
# Line 1714  namespace LinuxSampler { namespace gig { Line 2206  namespace LinuxSampler { namespace gig {
2206      }      }
2207    
2208      String Engine::Description() {      String Engine::Description() {
2209          return "Gigasampler Engine";          return "Gigasampler Format Engine";
2210      }      }
2211    
2212      String Engine::Version() {      String Engine::Version() {
2213          String s = "$Revision: 1.74 $";          String s = "$Revision: 1.100 $";
2214          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
2215      }      }
2216    
# Line 1727  namespace LinuxSampler { namespace gig { Line 2219  namespace LinuxSampler { namespace gig {
2219      }      }
2220    
2221      // static constant initializers      // static constant initializers
2222      const float* Engine::VolumeCurve(InitVolumeCurve());      const Engine::FloatTable Engine::VolumeCurve(InitVolumeCurve());
2223      const float* Engine::PanCurve(InitPanCurve());      const Engine::FloatTable Engine::PanCurve(InitPanCurve());
2224      const float* Engine::CrossfadeCurve(InitCrossfadeCurve());      const Engine::FloatTable Engine::CrossfadeCurve(InitCrossfadeCurve());
2225    
2226      float* Engine::InitVolumeCurve() {      float* Engine::InitVolumeCurve() {
2227          // line-segment approximation          // line-segment approximation
# Line 1768  namespace LinuxSampler { namespace gig { Line 2260  namespace LinuxSampler { namespace gig {
2260          return y;          return y;
2261      }      }
2262    
     /**  
      * Changes the instrument for an engine channel.  
      *  
      * @param pEngineChannel - engine channel on which the instrument  
      *                         should be changed  
      * @param pInstrument - new instrument  
      * @returns a list of dimension regions from the old instrument  
      *          that are still in use  
      */  
     ::gig::DimensionRegion** Engine::ChangeInstrument(EngineChannel* pEngineChannel, ::gig::Instrument* pInstrument) {  
         instrument_change_command_t command;  
         command.pEngineChannel = pEngineChannel;  
         command.pInstrument = pInstrument;  
         InstrumentChangeQueue->push(&command);  
   
         // wait for the audio thread to confirm that the instrument  
         // change has been done  
         instrument_change_reply_t reply;  
         while (InstrumentChangeReplyQueue->pop(&reply) == 0) {  
             usleep(10000);  
         }  
         return pDimRegionsInUse;  
     }  
   
2263  }} // namespace LinuxSampler::gig  }} // namespace LinuxSampler::gig

Legend:
Removed from v.1043  
changed lines
  Added in v.1843

  ViewVC Help
Powered by ViewVC