/[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 1038 by persson, Sat Feb 3 15:33:00 2007 UTC revision 1715 by schoenebeck, Tue Mar 11 15:20:46 2008 UTC
# Line 2  Line 2 
2   *                                                                         *   *                                                                         *
3   *   LinuxSampler - modular, streaming capable sampler                     *   *   LinuxSampler - modular, streaming capable sampler                     *
4   *                                                                         *   *                                                                         *
5   *   Copyright (C) 2003,2004 by Benno Senoner and Christian Schoenebeck   *   *   Copyright (C) 2003,2004 by Benno Senoner and Christian Schoenebeck    *
6   *   Copyright (C) 2005-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 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");
64              pEngine->Connect(pDevice);              pEngine->Connect(pDevice);
65              engines[pDevice] = pEngine;              engines[pDevice] = pEngine;
66          }          }
   
67          // register engine channel to the engine instance          // register engine channel to the engine instance
   
         // Disable the engine while the new engine channel is added  
         // and initialized. The engine will be enabled again in  
         // EngineChannel::Connect.  
         pEngine->DisableAndLock();  
   
68          pEngine->engineChannels.add(pChannel);          pEngine->engineChannels.add(pChannel);
69          // remember index in the ArrayList          // remember index in the ArrayList
70          pChannel->iEngineIndexSelf = pEngine->engineChannels.size() - 1;          pChannel->iEngineIndexSelf = pEngine->engineChannels.size() - 1;
# Line 74  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 100  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;
# Line 108  namespace LinuxSampler { namespace gig { Line 108  namespace LinuxSampler { namespace gig {
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>(CONFIG_MAX_VOICES);
111          pDimRegionsInUse   = new ::gig::DimensionRegion*[CONFIG_MAX_VOICES + 1];          pDimRegionPool[0]  = new Pool< ::gig::DimensionRegion*>(CONFIG_MAX_VOICES);
112            pDimRegionPool[1]  = new Pool< ::gig::DimensionRegion*>(CONFIG_MAX_VOICES);
113          pVoiceStealingQueue = new RTList<Event>(pEventPool);          pVoiceStealingQueue = new RTList<Event>(pEventPool);
114          pGlobalEvents      = new RTList<Event>(pEventPool);          pGlobalEvents      = new RTList<Event>(pEventPool);
         InstrumentChangeQueue      = new RingBuffer<instrument_change_command_t,false>(1, 0);  
         InstrumentChangeReplyQueue = new RingBuffer<instrument_change_reply_t,false>(1, 0);  
115    
116          for (RTList<Voice>::Iterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) {          for (RTList<Voice>::Iterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) {
117              iterVoice->SetEngine(this);              iterVoice->SetEngine(this);
# Line 121  namespace LinuxSampler { namespace gig { Line 120  namespace LinuxSampler { namespace gig {
120    
121          ResetInternal();          ResetInternal();
122          ResetScaleTuning();          ResetScaleTuning();
123            ResetSuspendedRegions();
124      }      }
125    
126      /**      /**
# Line 143  namespace LinuxSampler { namespace gig { Line 143  namespace LinuxSampler { namespace gig {
143          if (pEventGenerator) delete pEventGenerator;          if (pEventGenerator) delete pEventGenerator;
144          if (pVoiceStealingQueue) delete pVoiceStealingQueue;          if (pVoiceStealingQueue) delete pVoiceStealingQueue;
145          if (pSysexBuffer) delete pSysexBuffer;          if (pSysexBuffer) delete pSysexBuffer;
146            if (pGlobalEvents) delete pGlobalEvents;
147            if (pDimRegionPool[0]) delete pDimRegionPool[0];
148            if (pDimRegionPool[1]) delete pDimRegionPool[1];
149            ResetSuspendedRegions();
150          Unregister();          Unregister();
151      }      }
152    
# Line 152  namespace LinuxSampler { namespace gig { Line 156  namespace LinuxSampler { namespace gig {
156          dmsg(3,("gig::Engine: enabled (val=%d)\n", EngineDisabled.GetUnsafe()));          dmsg(3,("gig::Engine: enabled (val=%d)\n", EngineDisabled.GetUnsafe()));
157      }      }
158    
159        /**
160         * Temporarily stop the engine to not do anything. The engine will just be
161         * frozen during that time, that means after enabling it again it will
162         * continue where it was, with all its voices and playback state it had at
163         * the point of disabling. Notice that the engine's (audio) thread will
164         * continue to run, it just remains in an inactive loop during that time.
165         *
166         * If you need to be sure that all voices and disk streams are killed as
167         * well, use @c SuspendAll() instead.
168         *
169         * @see Enable(), SuspendAll()
170         */
171      void Engine::Disable() {      void Engine::Disable() {
172          dmsg(3,("gig::Engine: disabling\n"));          dmsg(3,("gig::Engine: disabling\n"));
173          bool* pWasDisabled = EngineDisabled.PushAndUnlock(true, 2); // wait max. 2s          bool* pWasDisabled = EngineDisabled.PushAndUnlock(true, 2); // wait max. 2s
# Line 165  namespace LinuxSampler { namespace gig { Line 181  namespace LinuxSampler { namespace gig {
181      }      }
182    
183      /**      /**
184         * Similar to @c Disable() but this method additionally kills all voices
185         * and disk streams and blocks until all voices and disk streams are actually
186         * killed / deleted.
187         *
188         * @e Note: only the original calling thread is able to re-enable the
189         * engine afterwards by calling @c ResumeAll() later on!
190         */
191        void Engine::SuspendAll() {
192            dmsg(2,("gig::Engine: Suspending all ...\n"));
193            // stop the engine, so we can safely modify the engine's
194            // data structures from this foreign thread
195            DisableAndLock();
196            // we could also use the respective class member variable here,
197            // but this is probably safer and cleaner
198            int iPendingStreamDeletions = 0;
199            // kill all voices on all engine channels the *die hard* way
200            for (int iChannel = 0; iChannel < engineChannels.size(); iChannel++) {
201                EngineChannel* pEngineChannel = engineChannels[iChannel];
202                RTList<uint>::Iterator iuiKey = pEngineChannel->pActiveKeys->first();
203                RTList<uint>::Iterator end    = pEngineChannel->pActiveKeys->end();
204                for (; iuiKey != end; ++iuiKey) { // iterate through all active keys
205                    midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[*iuiKey];
206                    RTList<Voice>::Iterator itVoice = pKey->pActiveVoices->first();
207                    RTList<Voice>::Iterator itVoicesEnd = pKey->pActiveVoices->end();
208                    for (; itVoice != itVoicesEnd; ++itVoice) { // iterate through all voices on this key
209                        // request a notification from disk thread side for stream deletion
210                        const Stream::Handle hStream = itVoice->KillImmediately(true);
211                        if (hStream != Stream::INVALID_HANDLE) { // voice actually used a stream
212                            iPendingStreamDeletions++;
213                        }
214                    }
215                }
216            }
217            // wait until all streams were actually deleted by the disk thread
218            while (iPendingStreamDeletions) {
219                while (
220                    iPendingStreamDeletions &&
221                    pDiskThread->AskForDeletedStream() != Stream::INVALID_HANDLE
222                ) iPendingStreamDeletions--;
223                if (!iPendingStreamDeletions) break;
224                usleep(10000); // sleep for 10ms
225            }
226            dmsg(2,("gig::Engine: Everything suspended.\n"));
227        }
228    
229        /**
230         * At the moment same as calling @c Enable() directly, but this might
231         * change in future, so better call this method as counterpart to
232         * @c SuspendAll() instead of @c Enable() !
233         */
234        void Engine::ResumeAll() {
235            Enable();
236        }
237    
238        /**
239         * Order the engine to stop rendering audio for the given region.
240         * Additionally this method will block until all voices and their disk
241         * streams associated with that region are actually killed / deleted, so
242         * one can i.e. safely modify the region with an instrument editor after
243         * returning from this method.
244         *
245         * @param pRegion - region the engine shall stop using
246         */
247        void Engine::Suspend(::gig::Region* pRegion) {
248            dmsg(2,("gig::Engine: Suspending Region %x ...\n",pRegion));
249            SuspendedRegionsMutex.Lock();
250            SuspensionChangeOngoing.Set(true);
251            pPendingRegionSuspension = pRegion;
252            SuspensionChangeOngoing.WaitAndUnlockIf(true);
253            SuspendedRegionsMutex.Unlock();
254            dmsg(2,("gig::Engine: Region %x suspended.",pRegion));
255        }
256    
257        /**
258         * Orders the engine to resume playing back the given region, previously
259         * suspended with @c Suspend() .
260         *
261         * @param pRegion - region the engine shall be allowed to use again
262         */
263        void Engine::Resume(::gig::Region* pRegion) {
264            dmsg(2,("gig::Engine: Resuming Region %x ...\n",pRegion));
265            SuspendedRegionsMutex.Lock();
266            SuspensionChangeOngoing.Set(true);
267            pPendingRegionResumption = pRegion;
268            SuspensionChangeOngoing.WaitAndUnlockIf(true);
269            SuspendedRegionsMutex.Unlock();
270            dmsg(2,("gig::Engine: Region %x resumed.\n",pRegion));
271        }
272    
273        /**
274       *  Reset all voices and disk thread and clear input event queue and all       *  Reset all voices and disk thread and clear input event queue and all
275       *  control and status variables.       *  control and status variables.
276       */       */
# Line 219  namespace LinuxSampler { namespace gig { Line 325  namespace LinuxSampler { namespace gig {
325          memset(&ScaleTuning[0], 0x00, 12);          memset(&ScaleTuning[0], 0x00, 12);
326      }      }
327    
328        void Engine::ResetSuspendedRegions() {
329            SuspendedRegions.clear();
330            iPendingStreamDeletions = 0;
331            pPendingRegionSuspension = pPendingRegionResumption = NULL;
332            SuspensionChangeOngoing.Set(false);
333        }
334    
335      /**      /**
336       * Connect this engine instance with the given audio output device.       * Connect this engine instance with the given audio output device.
337       * This method will be called when an Engine instance is created.       * This method will be called when an Engine instance is created.
# Line 298  namespace LinuxSampler { namespace gig { Line 411  namespace LinuxSampler { namespace gig {
411      }      }
412    
413      /**      /**
414         * Called by the engine's (audio) thread once per cycle to process requests
415         * from the outer world to suspend or resume a given @c gig::Region .
416         */
417        void Engine::ProcessSuspensionsChanges() {
418            // process request for suspending one region
419            if (pPendingRegionSuspension) {
420                // kill all voices on all engine channels that use this region
421                for (int iChannel = 0; iChannel < engineChannels.size(); iChannel++) {
422                    EngineChannel* pEngineChannel = engineChannels[iChannel];
423                    RTList<uint>::Iterator iuiKey = pEngineChannel->pActiveKeys->first();
424                    RTList<uint>::Iterator end    = pEngineChannel->pActiveKeys->end();
425                    for (; iuiKey != end; ++iuiKey) { // iterate through all active keys
426                        midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[*iuiKey];
427                        RTList<Voice>::Iterator itVoice = pKey->pActiveVoices->first();
428                        // if current key is not associated with this region, skip this key
429                        if (itVoice->pDimRgn->GetParent() != pPendingRegionSuspension) continue;
430                        RTList<Voice>::Iterator itVoicesEnd = pKey->pActiveVoices->end();
431                        for (; itVoice != itVoicesEnd; ++itVoice) { // iterate through all voices on this key
432                            // request a notification from disk thread side for stream deletion
433                            const Stream::Handle hStream = itVoice->KillImmediately(true);
434                            if (hStream != Stream::INVALID_HANDLE) { // voice actually used a stream
435                                iPendingStreamDeletions++;
436                            }
437                        }
438                    }
439                }
440                // make sure the region is not yet on the list
441                bool bAlreadySuspended = false;
442                RTList< ::gig::Region*>::Iterator iter = SuspendedRegions.first();
443                RTList< ::gig::Region*>::Iterator end  = SuspendedRegions.end();
444                for (; iter != end; ++iter) { // iterate through all suspended regions
445                    if (*iter == pPendingRegionSuspension) { // found
446                        bAlreadySuspended = true;
447                        dmsg(1,("gig::Engine: attempt to suspend an already suspended region !!!\n"));
448                        break;
449                    }
450                }
451                if (!bAlreadySuspended) {
452                    // put the region on the list of suspended regions
453                    RTList< ::gig::Region*>::Iterator iter = SuspendedRegions.allocAppend();
454                    if (iter) {
455                        *iter = pPendingRegionSuspension;
456                    } else std::cerr << "gig::Engine: Could not suspend Region, list is full. This is a bug!!!\n" << std::flush;
457                }
458                // free request slot for next caller (and to make sure that
459                // we're not going to process the same request in the next cycle)
460                pPendingRegionSuspension = NULL;
461                // if no disk stream deletions are pending, awaken other side, as
462                // we're done in this case
463                if (!iPendingStreamDeletions) SuspensionChangeOngoing.Set(false);
464            }
465    
466            // process request for resuming one region
467            if (pPendingRegionResumption) {
468                // remove region from the list of suspended regions
469                RTList< ::gig::Region*>::Iterator iter = SuspendedRegions.first();
470                RTList< ::gig::Region*>::Iterator end  = SuspendedRegions.end();
471                for (; iter != end; ++iter) { // iterate through all suspended regions
472                    if (*iter == pPendingRegionResumption) { // found
473                        SuspendedRegions.free(iter);
474                        break; // done
475                    }
476                }
477                // free request slot for next caller
478                pPendingRegionResumption = NULL;
479                // awake other side as we're done
480                SuspensionChangeOngoing.Set(false);
481            }
482        }
483    
484        /**
485         * Called by the engine's (audio) thread once per cycle to check if
486         * streams of voices that were killed due to suspension request have
487         * finally really been deleted by the disk thread.
488         */
489        void Engine::ProcessPendingStreamDeletions() {
490            if (!iPendingStreamDeletions) return;
491            //TODO: or shall we better store a list with stream handles instead of a scalar amount of streams to be deleted? might be safer
492            while (
493                iPendingStreamDeletions &&
494                pDiskThread->AskForDeletedStream() != Stream::INVALID_HANDLE
495            ) iPendingStreamDeletions--;
496            // just for safety ...
497            while (pDiskThread->AskForDeletedStream() != Stream::INVALID_HANDLE);
498            // now that all disk streams are deleted, awake other side as
499            // we're finally done with suspending the requested region
500            if (!iPendingStreamDeletions) SuspensionChangeOngoing.Set(false);
501        }
502    
503        /**
504         * Returns @c true if the given region is currently set to be suspended
505         * from being used, @c false otherwise.
506         */
507        bool Engine::RegionSuspended(::gig::Region* pRegion) {
508            if (SuspendedRegions.isEmpty()) return false;
509            //TODO: or shall we use a sorted container instead of the RTList? might be faster ... or trivial ;-)
510            RTList< ::gig::Region*>::Iterator iter = SuspendedRegions.first();
511            RTList< ::gig::Region*>::Iterator end  = SuspendedRegions.end();
512            for (; iter != end; ++iter)  // iterate through all suspended regions
513                if (*iter == pRegion) return true;
514            return false;
515        }
516    
517        /**
518       * Clear all engine global event lists.       * Clear all engine global event lists.
519       */       */
520      void Engine::ClearEventLists() {      void Engine::ClearEventLists() {
# Line 352  namespace LinuxSampler { namespace gig { Line 569  namespace LinuxSampler { namespace gig {
569       *  @returns       0 on success       *  @returns       0 on success
570       */       */
571      int Engine::RenderAudio(uint Samples) {      int Engine::RenderAudio(uint Samples) {
572          dmsg(7,("RenderAudio(Samples=%d)\n", Samples));          dmsg(8,("RenderAudio(Samples=%d)\n", Samples));
573    
574          // return if engine disabled          // return if engine disabled
575          if (EngineDisabled.Pop()) {          if (EngineDisabled.Pop()) {
# Line 360  namespace LinuxSampler { namespace gig { Line 577  namespace LinuxSampler { namespace gig {
577              return 0;              return 0;
578          }          }
579    
580            // process requests for suspending / resuming regions (i.e. to avoid
581            // crashes while these regions are modified by an instrument editor)
582            ProcessSuspensionsChanges();
583    
584          // update time of start and end of this audio fragment (as events' time stamps relate to this)          // update time of start and end of this audio fragment (as events' time stamps relate to this)
585          pEventGenerator->UpdateFragmentTime(Samples);          pEventGenerator->UpdateFragmentTime(Samples);
586    
# Line 390  namespace LinuxSampler { namespace gig { Line 611  namespace LinuxSampler { namespace gig {
611          ActiveVoiceCountTemp = 0;          ActiveVoiceCountTemp = 0;
612    
613          // handle instrument change commands          // handle instrument change commands
614          instrument_change_command_t command;          bool instrumentChanged = false;
615          if (InstrumentChangeQueue->pop(&command) > 0) {          for (int i = 0; i < engineChannels.size(); i++) {
616              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;  
617    
618                  RTList<Voice>::Iterator itVoice     = pKey->pActiveVoices->first();              // as we're going to (carefully) write some status to the
619                  RTList<Voice>::Iterator itVoicesEnd = pKey->pActiveVoices->end();              // synchronized struct, we cast away the const
620                  for (; itVoice != itVoicesEnd; ++itVoice) { // iterate through all voices on this key              EngineChannel::instrument_change_command_t& cmd =
621                      if (!itVoice->Orphan) {                  const_cast<EngineChannel::instrument_change_command_t&>(pEngineChannel->InstrumentChangeCommandReader.Lock());
622    
623                pEngineChannel->pDimRegionsInUse = cmd.pDimRegionsInUse;
624                pEngineChannel->pDimRegionsInUse->clear();
625    
626                if (cmd.bChangeInstrument) {
627                    // change instrument
628                    dmsg(5,("Engine: instrument change command received\n"));
629                    cmd.bChangeInstrument = false;
630                    pEngineChannel->pInstrument = cmd.pInstrument;
631                    instrumentChanged = true;
632    
633                    // Iterate through all active voices and mark them as
634                    // "orphans", which means that the dimension regions
635                    // and samples they use should be released to the
636                    // instrument resource manager when the voices die.
637                    int i = 0;
638                    RTList<uint>::Iterator iuiKey = pEngineChannel->pActiveKeys->first();
639                    RTList<uint>::Iterator end    = pEngineChannel->pActiveKeys->end();
640                    while (iuiKey != end) { // iterate through all active keys
641                        midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[*iuiKey];
642                        ++iuiKey;
643    
644                        RTList<Voice>::Iterator itVoice     = pKey->pActiveVoices->first();
645                        RTList<Voice>::Iterator itVoicesEnd = pKey->pActiveVoices->end();
646                        for (; itVoice != itVoicesEnd; ++itVoice) { // iterate through all voices on this key
647                          itVoice->Orphan = true;                          itVoice->Orphan = true;
                         pDimRegionsInUse[i++] = itVoice->pDimRgn;  
648                      }                      }
649                  }                  }
650              }              }
651              pDimRegionsInUse[i] = 0; // end of list          }
652            if (instrumentChanged) {
653              // 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
654              instrument_change_reply_t reply;              ResetSuspendedRegions();
             InstrumentChangeReplyQueue->push(&reply);  
655          }          }
656    
657          // handle events on all engine channels          // handle events on all engine channels
# Line 457  namespace LinuxSampler { namespace gig { Line 689  namespace LinuxSampler { namespace gig {
689          ActiveVoiceCount = ActiveVoiceCountTemp;          ActiveVoiceCount = ActiveVoiceCountTemp;
690          if (ActiveVoiceCount > ActiveVoiceCountMax) ActiveVoiceCountMax = ActiveVoiceCount;          if (ActiveVoiceCount > ActiveVoiceCountMax) ActiveVoiceCountMax = ActiveVoiceCount;
691    
692            // in case regions were previously suspended and we killed voices
693            // with disk streams due to that, check if those streams have finally
694            // been deleted by the disk thread
695            if (iPendingStreamDeletions) ProcessPendingStreamDeletions();
696    
697            for (int i = 0; i < engineChannels.size(); i++) {
698                engineChannels[i]->InstrumentChangeCommandReader.Unlock();
699            }
700          FrameTime += Samples;          FrameTime += Samples;
701    
702          return 0;          return 0;
# Line 524  namespace LinuxSampler { namespace gig { Line 764  namespace LinuxSampler { namespace gig {
764          if (pEngineChannel->GetMute()) return; // skip if sampler channel is muted          if (pEngineChannel->GetMute()) return; // skip if sampler channel is muted
765          #endif          #endif
766    
767            uint voiceCount = 0;
768            uint streamCount = 0;
769          RTList<uint>::Iterator iuiKey = pEngineChannel->pActiveKeys->first();          RTList<uint>::Iterator iuiKey = pEngineChannel->pActiveKeys->first();
770          RTList<uint>::Iterator end    = pEngineChannel->pActiveKeys->end();          RTList<uint>::Iterator end    = pEngineChannel->pActiveKeys->end();
771          while (iuiKey != end) { // iterate through all active keys          while (iuiKey != end) { // iterate through all active keys
# Line 535  namespace LinuxSampler { namespace gig { Line 777  namespace LinuxSampler { namespace gig {
777              for (; itVoice != itVoicesEnd; ++itVoice) { // iterate through all voices on this key              for (; itVoice != itVoicesEnd; ++itVoice) { // iterate through all voices on this key
778                  // now render current voice                  // now render current voice
779                  itVoice->Render(Samples);                  itVoice->Render(Samples);
780                  if (itVoice->IsActive()) ActiveVoiceCountTemp++; // still active                  if (itVoice->IsActive()) { // still active
781                  else { // voice reached end, is now inactive                      if (!itVoice->Orphan) {
782                            *(pEngineChannel->pDimRegionsInUse->allocAppend()) = itVoice->pDimRgn;
783                        }
784                        ActiveVoiceCountTemp++;
785                        voiceCount++;
786    
787                        if (itVoice->PlaybackState == Voice::playback_state_disk) {
788                            if ((itVoice->DiskStreamRef).State == Stream::state_active) streamCount++;
789                        }
790                    }  else { // voice reached end, is now inactive
791                      FreeVoice(pEngineChannel, itVoice); // remove voice from the list of active voices                      FreeVoice(pEngineChannel, itVoice); // remove voice from the list of active voices
792                  }                  }
793              }              }
794          }          }
795    
796            pEngineChannel->SetVoiceCount(voiceCount);
797            pEngineChannel->SetDiskStreamCount(streamCount);
798      }      }
799    
800      /**      /**
# Line 566  namespace LinuxSampler { namespace gig { Line 820  namespace LinuxSampler { namespace gig {
820                  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);
821              if (itNewVoice) {              if (itNewVoice) {
822                  itNewVoice->Render(Samples);                  itNewVoice->Render(Samples);
823                  if (itNewVoice->IsActive()) ActiveVoiceCountTemp++; // still active                  if (itNewVoice->IsActive()) { // still active
824                  else { // voice reached end, is now inactive                      *(pEngineChannel->pDimRegionsInUse->allocAppend()) = itNewVoice->pDimRgn;
825                        ActiveVoiceCountTemp++;
826                        pEngineChannel->SetVoiceCount(pEngineChannel->GetVoiceCount() + 1);
827    
828                        if (itNewVoice->PlaybackState == Voice::playback_state_disk) {
829                            if (itNewVoice->DiskStreamRef.State == Stream::state_active) {
830                                pEngineChannel->SetDiskStreamCount(pEngineChannel->GetDiskStreamCount() + 1);
831                            }
832                        }
833                    } else { // voice reached end, is now inactive
834                      FreeVoice(pEngineChannel, itNewVoice); // remove voice from the list of active voices                      FreeVoice(pEngineChannel, itNewVoice); // remove voice from the list of active voices
835                  }                  }
836              }              }
# Line 711  namespace LinuxSampler { namespace gig { Line 974  namespace LinuxSampler { namespace gig {
974    
975          if (!pEngineChannel->pInstrument) return; // ignore if no instrument loaded          if (!pEngineChannel->pInstrument) return; // ignore if no instrument loaded
976    
977            //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
978            itNoteOnEvent->Param.Note.Key += pEngineChannel->GlobalTranspose;
979    
980          const int key = itNoteOnEvent->Param.Note.Key;          const int key = itNoteOnEvent->Param.Note.Key;
981          midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[key];          midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[key];
982    
# Line 768  namespace LinuxSampler { namespace gig { Line 1034  namespace LinuxSampler { namespace gig {
1034          {          {
1035              // first, get total amount of required voices (dependant on amount of layers)              // first, get total amount of required voices (dependant on amount of layers)
1036              ::gig::Region* pRegion = pEngineChannel->pInstrument->GetRegion(itNoteOnEventOnKeyList->Param.Note.Key);              ::gig::Region* pRegion = pEngineChannel->pInstrument->GetRegion(itNoteOnEventOnKeyList->Param.Note.Key);
1037              if (pRegion) {              if (pRegion && !RegionSuspended(pRegion)) {
1038                  int voicesRequired = pRegion->Layers;                  int voicesRequired = pRegion->Layers;
1039                  // now launch the required amount of voices                  // now launch the required amount of voices
1040                  for (int i = 0; i < voicesRequired; i++)                  for (int i = 0; i < voicesRequired; i++)
# Line 798  namespace LinuxSampler { namespace gig { Line 1064  namespace LinuxSampler { namespace gig {
1064          if (pEngineChannel->GetMute()) return; // skip if sampler channel is muted          if (pEngineChannel->GetMute()) return; // skip if sampler channel is muted
1065          #endif          #endif
1066    
1067            //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
1068            itNoteOffEvent->Param.Note.Key += pEngineChannel->GlobalTranspose;
1069    
1070          const int iKey = itNoteOffEvent->Param.Note.Key;          const int iKey = itNoteOffEvent->Param.Note.Key;
1071          midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[iKey];          midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[iKey];
1072          pKey->KeyPressed = false; // the MIDI key was now released          pKey->KeyPressed = false; // the MIDI key was now released
# Line 1349  namespace LinuxSampler { namespace gig { Line 1618  namespace LinuxSampler { namespace gig {
1618      void Engine::ProcessControlChange(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itControlChangeEvent) {      void Engine::ProcessControlChange(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itControlChangeEvent) {
1619          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));
1620    
1621            // handle the "control triggered" MIDI rule: a control change
1622            // event can trigger a new note on or note off event
1623            if (pEngineChannel->pInstrument) {
1624    
1625                ::gig::MidiRule* rule;
1626                for (int i = 0 ; (rule = pEngineChannel->pInstrument->GetMidiRule(i)) ; i++) {
1627    
1628                    if (::gig::MidiRuleCtrlTrigger* ctrlTrigger =
1629                        dynamic_cast< ::gig::MidiRuleCtrlTrigger*>(rule)) {
1630                        if (itControlChangeEvent->Param.CC.Controller ==
1631                            ctrlTrigger->ControllerNumber) {
1632    
1633                            uint8_t oldCCValue = pEngineChannel->ControllerTable[
1634                                itControlChangeEvent->Param.CC.Controller];
1635                            uint8_t newCCValue = itControlChangeEvent->Param.CC.Value;
1636    
1637                            for (int i = 0 ; i < ctrlTrigger->Triggers ; i++) {
1638                                ::gig::MidiRuleCtrlTrigger::trigger_t* pTrigger =
1639                                      &ctrlTrigger->pTriggers[i];
1640    
1641                                // check if the controller has passed the
1642                                // trigger point in the right direction
1643                                if ((pTrigger->Descending &&
1644                                     oldCCValue > pTrigger->TriggerPoint &&
1645                                     newCCValue <= pTrigger->TriggerPoint) ||
1646                                    (!pTrigger->Descending &&
1647                                     oldCCValue < pTrigger->TriggerPoint &&
1648                                     newCCValue >= pTrigger->TriggerPoint)) {
1649    
1650                                    RTList<Event>::Iterator itNewEvent = pGlobalEvents->allocAppend();
1651                                    if (itNewEvent) {
1652                                        *itNewEvent = *itControlChangeEvent;
1653                                        itNewEvent->Param.Note.Key = pTrigger->Key;
1654    
1655                                        if (pTrigger->NoteOff || pTrigger->Velocity == 0) {
1656                                            itNewEvent->Type = Event::type_note_off;
1657                                            itNewEvent->Param.Note.Velocity = 100;
1658    
1659                                            ProcessNoteOff(pEngineChannel, itNewEvent);
1660                                        } else {
1661                                            itNewEvent->Type = Event::type_note_on;
1662                                            //TODO: if Velocity is 255, the triggered velocity should
1663                                            // depend on how fast the controller is moving
1664                                            itNewEvent->Param.Note.Velocity =
1665                                                pTrigger->Velocity == 255 ? 100 :
1666                                                pTrigger->Velocity;
1667    
1668                                            ProcessNoteOn(pEngineChannel, itNewEvent);
1669                                        }
1670                                    }
1671                                    else dmsg(1,("Event pool emtpy!\n"));
1672                                }
1673                            }
1674                        }
1675                    }
1676                }
1677            }
1678    
1679          // update controller value in the engine channel's controller table          // update controller value in the engine channel's controller table
1680          pEngineChannel->ControllerTable[itControlChangeEvent->Param.CC.Controller] = itControlChangeEvent->Param.CC.Value;          pEngineChannel->ControllerTable[itControlChangeEvent->Param.CC.Controller] = itControlChangeEvent->Param.CC.Value;
1681    
# Line 1358  namespace LinuxSampler { namespace gig { Line 1685  namespace LinuxSampler { namespace gig {
1685                  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;
1686                  break;                  break;
1687              }              }
1688                case 6: { // data entry (currently only used for RPN controllers)
1689                    if (pEngineChannel->GetMidiRpnController() == 2) { // coarse tuning in half tones
1690                        int transpose = (int) itControlChangeEvent->Param.CC.Value - 64;
1691                        // limit to +- two octaves for now
1692                        transpose = RTMath::Min(transpose,  24);
1693                        transpose = RTMath::Max(transpose, -24);
1694                        pEngineChannel->GlobalTranspose = transpose;
1695                        // workaround, so we won't have hanging notes
1696                        ReleaseAllVoices(pEngineChannel, itControlChangeEvent);
1697                    }
1698                    // to avoid other MIDI CC #6 messages to be misenterpreted as RPN controller data
1699                    pEngineChannel->ResetMidiRpnController();
1700                    break;
1701                }
1702              case 7: { // volume              case 7: { // volume
1703                  //TODO: not sample accurate yet                  //TODO: not sample accurate yet
1704                  pEngineChannel->MidiVolume = VolumeCurve[itControlChangeEvent->Param.CC.Value];                  pEngineChannel->MidiVolume = VolumeCurve[itControlChangeEvent->Param.CC.Value];
# Line 1418  namespace LinuxSampler { namespace gig { Line 1759  namespace LinuxSampler { namespace gig {
1759                  break;                  break;
1760              }              }
1761              case 65: { // portamento on / off              case 65: { // portamento on / off
1762                  KillAllVoices(pEngineChannel, itControlChangeEvent);                  const bool bPortamento = itControlChangeEvent->Param.CC.Value >= 64;
1763                  pEngineChannel->PortamentoMode = itControlChangeEvent->Param.CC.Value >= 64;                  if (bPortamento != pEngineChannel->PortamentoMode)
1764                        KillAllVoices(pEngineChannel, itControlChangeEvent);
1765                    pEngineChannel->PortamentoMode = bPortamento;
1766                  break;                  break;
1767              }              }
1768              case 66: { // sostenuto              case 66: { // sostenuto
# Line 1462  namespace LinuxSampler { namespace gig { Line 1805  namespace LinuxSampler { namespace gig {
1805                  }                  }
1806                  break;                  break;
1807              }              }
1808                case 100: { // RPN controller LSB
1809                    pEngineChannel->SetMidiRpnControllerLsb(itControlChangeEvent->Param.CC.Value);
1810                    break;
1811                }
1812                case 101: { // RPN controller MSB
1813                    pEngineChannel->SetMidiRpnControllerMsb(itControlChangeEvent->Param.CC.Value);
1814                    break;
1815                }
1816    
1817    
1818              // Channel Mode Messages              // Channel Mode Messages
# Line 1481  namespace LinuxSampler { namespace gig { Line 1832  namespace LinuxSampler { namespace gig {
1832                  break;                  break;
1833              }              }
1834              case 126: { // mono mode on              case 126: { // mono mode on
1835                  KillAllVoices(pEngineChannel, itControlChangeEvent);                  if (!pEngineChannel->SoloMode)
1836                        KillAllVoices(pEngineChannel, itControlChangeEvent);
1837                  pEngineChannel->SoloMode = true;                  pEngineChannel->SoloMode = true;
1838                  break;                  break;
1839              }              }
1840              case 127: { // poly mode on              case 127: { // poly mode on
1841                  KillAllVoices(pEngineChannel, itControlChangeEvent);                  if (pEngineChannel->SoloMode)
1842                        KillAllVoices(pEngineChannel, itControlChangeEvent);
1843                  pEngineChannel->SoloMode = false;                  pEngineChannel->SoloMode = false;
1844                  break;                  break;
1845              }              }
# Line 1496  namespace LinuxSampler { namespace gig { Line 1849  namespace LinuxSampler { namespace gig {
1849          if (!pEngineChannel->fxSends.empty()) {          if (!pEngineChannel->fxSends.empty()) {
1850              for (int iFxSend = 0; iFxSend < pEngineChannel->GetFxSendCount(); iFxSend++) {              for (int iFxSend = 0; iFxSend < pEngineChannel->GetFxSendCount(); iFxSend++) {
1851                  FxSend* pFxSend = pEngineChannel->GetFxSend(iFxSend);                  FxSend* pFxSend = pEngineChannel->GetFxSend(iFxSend);
1852                  if (pFxSend->MidiController() == itControlChangeEvent->Param.CC.Controller)                  if (pFxSend->MidiController() == itControlChangeEvent->Param.CC.Controller) {
1853                      pFxSend->SetLevel(itControlChangeEvent->Param.CC.Value);                      pFxSend->SetLevel(itControlChangeEvent->Param.CC.Value);
1854                        pFxSend->SetInfoChanged(true);
1855                    }
1856              }              }
1857          }          }
1858      }      }
# Line 1690  namespace LinuxSampler { namespace gig { Line 2045  namespace LinuxSampler { namespace gig {
2045      }      }
2046    
2047      String Engine::Description() {      String Engine::Description() {
2048          return "Gigasampler Engine";          return "Gigasampler Format Engine";
2049      }      }
2050    
2051      String Engine::Version() {      String Engine::Version() {
2052          String s = "$Revision: 1.71 $";          String s = "$Revision: 1.89 $";
2053          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
2054      }      }
2055    
# Line 1703  namespace LinuxSampler { namespace gig { Line 2058  namespace LinuxSampler { namespace gig {
2058      }      }
2059    
2060      // static constant initializers      // static constant initializers
2061      const float* Engine::VolumeCurve(InitVolumeCurve());      const Engine::FloatTable Engine::VolumeCurve(InitVolumeCurve());
2062      const float* Engine::PanCurve(InitPanCurve());      const Engine::FloatTable Engine::PanCurve(InitPanCurve());
2063      const float* Engine::CrossfadeCurve(InitCrossfadeCurve());      const Engine::FloatTable Engine::CrossfadeCurve(InitCrossfadeCurve());
2064    
2065      float* Engine::InitVolumeCurve() {      float* Engine::InitVolumeCurve() {
2066          // line-segment approximation          // line-segment approximation
# Line 1744  namespace LinuxSampler { namespace gig { Line 2099  namespace LinuxSampler { namespace gig {
2099          return y;          return y;
2100      }      }
2101    
     /**  
      * 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;  
     }  
   
2102  }} // namespace LinuxSampler::gig  }} // namespace LinuxSampler::gig

Legend:
Removed from v.1038  
changed lines
  Added in v.1715

  ViewVC Help
Powered by ViewVC