/[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 1399 by schoenebeck, Thu Oct 11 18:53:29 2007 UTC revision 1860 by schoenebeck, Wed Mar 11 18:23:35 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-2009 Christian Schoenebeck                         *
7   *                                                                         *   *                                                                         *
8   *   This program is free software; you can redistribute it and/or modify  *   *   This program is free software; you can redistribute it and/or modify  *
9   *   it under the terms of the GNU General Public License as published by  *   *   it under the terms of the GNU General Public License as published by  *
# Line 29  Line 29 
29    
30  #include "Engine.h"  #include "Engine.h"
31    
32    #include "../../common/global_private.h"
33    
34  namespace LinuxSampler { namespace gig {  namespace LinuxSampler { namespace gig {
35    
36      InstrumentResourceManager Engine::instruments;      InstrumentResourceManager Engine::instruments;
# Line 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 105  namespace LinuxSampler { namespace gig { Line 107  namespace LinuxSampler { namespace gig {
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 143  namespace LinuxSampler { namespace gig { Line 145  namespace LinuxSampler { namespace gig {
145          if (pVoiceStealingQueue) delete pVoiceStealingQueue;          if (pVoiceStealingQueue) delete pVoiceStealingQueue;
146          if (pSysexBuffer) delete pSysexBuffer;          if (pSysexBuffer) delete pSysexBuffer;
147          if (pGlobalEvents) delete pGlobalEvents;          if (pGlobalEvents) delete pGlobalEvents;
148          if (InstrumentChangeQueue) delete InstrumentChangeQueue;          if (pDimRegionPool[0]) delete pDimRegionPool[0];
149          if (InstrumentChangeReplyQueue) delete InstrumentChangeReplyQueue;          if (pDimRegionPool[1]) delete pDimRegionPool[1];
         if (pDimRegionsInUse) delete[] pDimRegionsInUse;  
150          ResetSuspendedRegions();          ResetSuspendedRegions();
151          Unregister();          Unregister();
152      }      }
# Line 291  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 342  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 358  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 381  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 408  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      /**      /**
# Line 458  namespace LinuxSampler { namespace gig { Line 467  namespace LinuxSampler { namespace gig {
467              // free request slot for next caller (and to make sure that              // 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)              // we're not going to process the same request in the next cycle)
469              pPendingRegionSuspension = NULL;              pPendingRegionSuspension = NULL;
470              // if no disk stream deletions are pending, awaker other side, as              // if no disk stream deletions are pending, awaken other side, as
471              // we're done in this case              // we're done in this case
472              if (!iPendingStreamDeletions) SuspensionChangeOngoing.Set(false);              if (!iPendingStreamDeletions) SuspensionChangeOngoing.Set(false);
473          }          }
# Line 569  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    
# Line 584  namespace LinuxSampler { namespace gig { Line 594  namespace LinuxSampler { namespace gig {
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 611  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;  
   
             //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  
             ResetSuspendedRegions();  
627    
628              // iterate through all active voices and mark their              // as we're going to (carefully) write some status to the
629              // dimension regions as "in use". The instrument resource              // synchronized struct, we cast away the const
630              // manager may delete all of the instrument except the              EngineChannel::instrument_change_command_t& cmd =
631              // dimension regions and samples that are in use.                  const_cast<EngineChannel::instrument_change_command_t&>(pEngineChannel->InstrumentChangeCommandReader.Lock());
632              int i = 0;  
633              RTList<uint>::Iterator iuiKey = pEngineChannel->pActiveKeys->first();              pEngineChannel->pDimRegionsInUse = cmd.pDimRegionsInUse;
634              RTList<uint>::Iterator end    = pEngineChannel->pActiveKeys->end();              pEngineChannel->pDimRegionsInUse->clear();
635              while (iuiKey != end) { // iterate through all active keys  
636                  midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[*iuiKey];              if (cmd.bChangeInstrument) {
637                  ++iuiKey;                  // 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();                      RTList<Voice>::Iterator itVoice     = pKey->pActiveVoices->first();
655                  RTList<Voice>::Iterator itVoicesEnd = pKey->pActiveVoices->end();                      RTList<Voice>::Iterator itVoicesEnd = pKey->pActiveVoices->end();
656                  for (; itVoice != itVoicesEnd; ++itVoice) { // iterate through all voices on this key                      for (; itVoice != itVoicesEnd; ++itVoice) { // iterate through all voices on this key
                     if (!itVoice->Orphan) {  
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 678  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          // in case regions were previously suspended and we killed voices
703          // with disk streams due to that, check if those streams have finally          // with disk streams due to that, check if those streams have finally
704          // been deleted by the disk thread          // been deleted by the disk thread
705          if (iPendingStreamDeletions) ProcessPendingStreamDeletions();          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 767  namespace LinuxSampler { namespace gig { Line 789  namespace LinuxSampler { namespace gig {
789                  // now render current voice                  // now render current voice
790                  itVoice->Render(Samples);                  itVoice->Render(Samples);
791                  if (itVoice->IsActive()) { // still active                  if (itVoice->IsActive()) { // still active
792                        if (!itVoice->Orphan) {
793                            *(pEngineChannel->pDimRegionsInUse->allocAppend()) = itVoice->pDimRgn;
794                        }
795                      ActiveVoiceCountTemp++;                      ActiveVoiceCountTemp++;
796                      voiceCount++;                      voiceCount++;
797    
798                      if (itVoice->PlaybackState == Voice::playback_state_disk) {                      if (itVoice->PlaybackState == Voice::playback_state_disk) {
799                          if ((itVoice->DiskStreamRef).State == Stream::state_active) streamCount++;                          if ((itVoice->DiskStreamRef).State != Stream::state_unused) streamCount++;
800                      }                      }
801                  }  else { // voice reached end, is now inactive                  }  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
# Line 807  namespace LinuxSampler { namespace gig { Line 832  namespace LinuxSampler { namespace gig {
832              if (itNewVoice) {              if (itNewVoice) {
833                  itNewVoice->Render(Samples);                  itNewVoice->Render(Samples);
834                  if (itNewVoice->IsActive()) { // still active                  if (itNewVoice->IsActive()) { // still active
835                        *(pEngineChannel->pDimRegionsInUse->allocAppend()) = itNewVoice->pDimRgn;
836                      ActiveVoiceCountTemp++;                      ActiveVoiceCountTemp++;
837                      pEngineChannel->SetVoiceCount(pEngineChannel->GetVoiceCount() + 1);                      pEngineChannel->SetVoiceCount(pEngineChannel->GetVoiceCount() + 1);
838    
839                      if (itNewVoice->PlaybackState == Voice::playback_state_disk) {                      if (itNewVoice->PlaybackState == Voice::playback_state_disk) {
840                          if (itNewVoice->DiskStreamRef.State == Stream::state_active) {                          if (itNewVoice->DiskStreamRef.State != Stream::state_unused) {
841                              pEngineChannel->SetDiskStreamCount(pEngineChannel->GetDiskStreamCount() + 1);                              pEngineChannel->SetDiskStreamCount(pEngineChannel->GetDiskStreamCount() + 1);
842                          }                          }
843                      }                      }
# Line 842  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 853  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 920  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 1603  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 1636  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 1776  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);                      pFxSend->SetInfoChanged(true);
1889                    }
1890              }              }
1891          }          }
1892      }      }
# Line 1797  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 1812  namespace LinuxSampler { namespace gig { Line 1957  namespace LinuxSampler { namespace gig {
1957                  if (reader.read(&addr[0], 3) != 3) goto free_sysex_data;                  if (reader.read(&addr[0], 3) != 3) goto free_sysex_data;
1958                  if (addr[0] == 0x40 && addr[1] == 0x00) { // System Parameters                  if (addr[0] == 0x40 && addr[1] == 0x00) { // System Parameters
1959                      dmsg(3,("\tSystem Parameter\n"));                      dmsg(3,("\tSystem Parameter\n"));
1960                        if (addr[2] == 0x7f) { // GS reset
1961                            for (int i = 0; i < engineChannels.size(); ++i) {
1962                                EngineChannel* pEngineChannel = engineChannels[i];
1963                                if (pEngineChannel->GetMidiInputPort() == itSysexEvent->pMidiInputPort) {
1964                                    KillAllVoices(pEngineChannel, itSysexEvent);
1965                                    pEngineChannel->ResetControllers();
1966                                }
1967                            }
1968                        }
1969                  }                  }
1970                  else if (addr[0] == 0x40 && addr[1] == 0x01) { // Common Parameters                  else if (addr[0] == 0x40 && addr[1] == 0x01) { // Common Parameters
1971                      dmsg(3,("\tCommon Parameter\n"));                      dmsg(3,("\tCommon Parameter\n"));
# Line 1833  namespace LinuxSampler { namespace gig { Line 1987  namespace LinuxSampler { namespace gig {
1987                              dmsg(3,("\t\t\tNew scale applied.\n"));                              dmsg(3,("\t\t\tNew scale applied.\n"));
1988                              break;                              break;
1989                          }                          }
1990                            case 0x15: { // chromatic / drumkit mode
1991                                dmsg(3,("\t\tMIDI Instrument Map Switch\n"));
1992                                uint8_t part = addr[1] & 0x0f;
1993                                uint8_t map;
1994                                if (!reader.pop(&map)) goto free_sysex_data;
1995                                for (int i = 0; i < engineChannels.size(); ++i) {
1996                                    EngineChannel* pEngineChannel = engineChannels[i];
1997                                    if (
1998                                        (pEngineChannel->midiChannel == part ||
1999                                         pEngineChannel->midiChannel == midi_chan_all) &&
2000                                         pEngineChannel->GetMidiInputPort() == itSysexEvent->pMidiInputPort
2001                                    ) {
2002                                        try {
2003                                            pEngineChannel->SetMidiInstrumentMap(map);
2004                                        } catch (Exception e) {
2005                                            dmsg(2,("\t\t\tCould not apply MIDI instrument map %d to part %d: %s\n", map, part, e.Message().c_str()));
2006                                            goto free_sysex_data;
2007                                        } catch (...) {
2008                                            dmsg(2,("\t\t\tCould not apply MIDI instrument map %d to part %d (unknown exception)\n", map, part));
2009                                            goto free_sysex_data;
2010                                        }
2011                                    }
2012                                }
2013                                dmsg(3,("\t\t\tApplied MIDI instrument map %d to part %d.\n", map, part));
2014                                break;
2015                            }
2016                      }                      }
2017                  }                  }
2018                  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 1939  namespace LinuxSampler { namespace gig { Line 2119  namespace LinuxSampler { namespace gig {
2119      }      }
2120    
2121      uint Engine::VoiceCount() {      uint Engine::VoiceCount() {
2122          return ActiveVoiceCount;          return atomic_read(&ActiveVoiceCount);
2123        }
2124    
2125        void Engine::SetVoiceCount(uint Count) {
2126            atomic_set(&ActiveVoiceCount, Count);
2127      }      }
2128    
2129      uint Engine::VoiceCountMax() {      uint Engine::VoiceCountMax() {
2130          return ActiveVoiceCountMax;          return ActiveVoiceCountMax;
2131      }      }
2132    
2133        int Engine::MaxVoices() {
2134            return pVoicePool->poolSize();
2135        }
2136    
2137        void Engine::SetMaxVoices(int iVoices) throw (Exception) {
2138            if (iVoices < 1)
2139                throw Exception("Maximum voices for an engine cannot be set lower than 1");
2140    
2141            SuspendAll();
2142    
2143            // NOTE: we need to clear pDimRegionsInUse before deleting pDimRegionPool,
2144            // otherwise memory corruption will occur if there are active voices (see bug #118)
2145            for (int iChannel = 0; iChannel < engineChannels.size(); iChannel++) {
2146                engineChannels[iChannel]->ClearDimRegionsInUse();
2147            }
2148    
2149            if (pDimRegionPool[0]) delete pDimRegionPool[0];
2150            if (pDimRegionPool[1]) delete pDimRegionPool[1];
2151    
2152            pDimRegionPool[0] = new Pool< ::gig::DimensionRegion*>(iVoices);
2153            pDimRegionPool[1] = new Pool< ::gig::DimensionRegion*>(iVoices);
2154    
2155            for (int iChannel = 0; iChannel < engineChannels.size(); iChannel++) {
2156                engineChannels[iChannel]->ResetDimRegionsInUse();
2157            }
2158    
2159            try {
2160                pVoicePool->resizePool(iVoices);
2161            } catch (...) {
2162                throw Exception("FATAL: Could not resize voice pool!");
2163            }
2164    
2165            for (RTList<Voice>::Iterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) {
2166                iterVoice->SetEngine(this);
2167                iterVoice->pDiskThread = this->pDiskThread;
2168            }
2169            pVoicePool->clear();
2170    
2171            ResumeAll();
2172        }
2173    
2174      bool Engine::DiskStreamSupported() {      bool Engine::DiskStreamSupported() {
2175          return true;          return true;
2176      }      }
2177    
2178      uint Engine::DiskStreamCount() {      uint Engine::DiskStreamCount() {
2179          return (pDiskThread) ? pDiskThread->ActiveStreamCount : 0;          return (pDiskThread) ? pDiskThread->GetActiveStreamCount() : 0;
2180      }      }
2181    
2182      uint Engine::DiskStreamCountMax() {      uint Engine::DiskStreamCountMax() {
2183          return (pDiskThread) ? pDiskThread->ActiveStreamCountMax : 0;          return (pDiskThread) ? pDiskThread->ActiveStreamCountMax : 0;
2184      }      }
2185    
2186        int Engine::MaxDiskStreams() {
2187            return iMaxDiskStreams;
2188        }
2189    
2190        void Engine::SetMaxDiskStreams(int iStreams) throw (Exception) {
2191            if (iStreams < 0)
2192                throw Exception("Maximum disk streams for an engine cannot be set lower than 0");
2193    
2194            SuspendAll();
2195    
2196            iMaxDiskStreams = iStreams;
2197    
2198            // reconnect to audio output device, because that will automatically
2199            // recreate the disk thread with the required amount of streams
2200            if (pAudioOutputDevice) Connect(pAudioOutputDevice);
2201    
2202            ResumeAll();
2203        }
2204    
2205      String Engine::DiskStreamBufferFillBytes() {      String Engine::DiskStreamBufferFillBytes() {
2206          return pDiskThread->GetBufferFillBytes();          return pDiskThread->GetBufferFillBytes();
2207      }      }
# Line 1971  namespace LinuxSampler { namespace gig { Line 2215  namespace LinuxSampler { namespace gig {
2215      }      }
2216    
2217      String Engine::Description() {      String Engine::Description() {
2218          return "Gigasampler Engine";          return "Gigasampler Format Engine";
2219      }      }
2220    
2221      String Engine::Version() {      String Engine::Version() {
2222          String s = "$Revision: 1.82 $";          String s = "$Revision: 1.101 $";
2223          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
2224      }      }
2225    
# Line 1984  namespace LinuxSampler { namespace gig { Line 2228  namespace LinuxSampler { namespace gig {
2228      }      }
2229    
2230      // static constant initializers      // static constant initializers
2231      const float* Engine::VolumeCurve(InitVolumeCurve());      const Engine::FloatTable Engine::VolumeCurve(InitVolumeCurve());
2232      const float* Engine::PanCurve(InitPanCurve());      const Engine::FloatTable Engine::PanCurve(InitPanCurve());
2233      const float* Engine::CrossfadeCurve(InitCrossfadeCurve());      const Engine::FloatTable Engine::CrossfadeCurve(InitCrossfadeCurve());
2234    
2235      float* Engine::InitVolumeCurve() {      float* Engine::InitVolumeCurve() {
2236          // line-segment approximation          // line-segment approximation
# Line 2025  namespace LinuxSampler { namespace gig { Line 2269  namespace LinuxSampler { namespace gig {
2269          return y;          return y;
2270      }      }
2271    
     /**  
      * 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;  
     }  
   
2272  }} // namespace LinuxSampler::gig  }} // namespace LinuxSampler::gig

Legend:
Removed from v.1399  
changed lines
  Added in v.1860

  ViewVC Help
Powered by ViewVC