/[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 1800 by schoenebeck, Sun Dec 7 01:26: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 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 1833  namespace LinuxSampler { namespace gig { Line 1978  namespace LinuxSampler { namespace gig {
1978                              dmsg(3,("\t\t\tNew scale applied.\n"));                              dmsg(3,("\t\t\tNew scale applied.\n"));
1979                              break;                              break;
1980                          }                          }
1981                            case 0x15: { // chromatic / drumkit mode
1982                                dmsg(3,("\t\tMIDI Instrument Map Switch\n"));
1983                                uint8_t part = addr[1] & 0x0f;
1984                                uint8_t map;
1985                                if (!reader.pop(&map)) goto free_sysex_data;
1986                                for (int i = 0; i < engineChannels.size(); ++i) {
1987                                    EngineChannel* pEngineChannel = engineChannels[i];
1988                                    if (
1989                                        (pEngineChannel->midiChannel == part ||
1990                                         pEngineChannel->midiChannel == midi_chan_all) &&
1991                                         pEngineChannel->GetMidiInputPort() == itSysexEvent->pMidiInputPort
1992                                    ) {
1993                                        try {
1994                                            pEngineChannel->SetMidiInstrumentMap(map);
1995                                        } catch (Exception e) {
1996                                            dmsg(2,("\t\t\tCould not apply MIDI instrument map %d to part %d: %s\n", map, part, e.Message().c_str()));
1997                                            goto free_sysex_data;
1998                                        } catch (...) {
1999                                            dmsg(2,("\t\t\tCould not apply MIDI instrument map %d to part %d (unknown exception)\n", map, part));
2000                                            goto free_sysex_data;
2001                                        }
2002                                    }
2003                                }
2004                                dmsg(3,("\t\t\tApplied MIDI instrument map %d to part %d.\n", map, part));
2005                                break;
2006                            }
2007                      }                      }
2008                  }                  }
2009                  else if (addr[0] == 0x40 && (addr[1] & 0xf0) == 0x20) { // Part Parameters (2)                  else if (addr[0] == 0x40 && (addr[1] & 0xf0) == 0x20) { // Part Parameters (2)
# Line 1939  namespace LinuxSampler { namespace gig { Line 2110  namespace LinuxSampler { namespace gig {
2110      }      }
2111    
2112      uint Engine::VoiceCount() {      uint Engine::VoiceCount() {
2113          return ActiveVoiceCount;          return atomic_read(&ActiveVoiceCount);
2114        }
2115    
2116        void Engine::SetVoiceCount(uint Count) {
2117            atomic_set(&ActiveVoiceCount, Count);
2118      }      }
2119    
2120      uint Engine::VoiceCountMax() {      uint Engine::VoiceCountMax() {
2121          return ActiveVoiceCountMax;          return ActiveVoiceCountMax;
2122      }      }
2123    
2124        int Engine::MaxVoices() {
2125            return pVoicePool->poolSize();
2126        }
2127    
2128        void Engine::SetMaxVoices(int iVoices) throw (Exception) {
2129            if (iVoices < 1)
2130                throw Exception("Maximum voices for an engine cannot be set lower than 1");
2131    
2132            SuspendAll();
2133    
2134            if (pDimRegionPool[0]) delete pDimRegionPool[0];
2135            if (pDimRegionPool[1]) delete pDimRegionPool[1];
2136    
2137            pDimRegionPool[0] = new Pool< ::gig::DimensionRegion*>(iVoices);
2138            pDimRegionPool[1] = new Pool< ::gig::DimensionRegion*>(iVoices);
2139    
2140            try {
2141                pVoicePool->resizePool(iVoices);
2142            } catch (...) {
2143                throw Exception("FATAL: Could not resize voice pool!");
2144            }
2145    
2146            for (RTList<Voice>::Iterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) {
2147                iterVoice->SetEngine(this);
2148                iterVoice->pDiskThread = this->pDiskThread;
2149            }
2150            pVoicePool->clear();
2151    
2152            ResumeAll();
2153        }
2154    
2155      bool Engine::DiskStreamSupported() {      bool Engine::DiskStreamSupported() {
2156          return true;          return true;
2157      }      }
2158    
2159      uint Engine::DiskStreamCount() {      uint Engine::DiskStreamCount() {
2160          return (pDiskThread) ? pDiskThread->ActiveStreamCount : 0;          return (pDiskThread) ? pDiskThread->GetActiveStreamCount() : 0;
2161      }      }
2162    
2163      uint Engine::DiskStreamCountMax() {      uint Engine::DiskStreamCountMax() {
2164          return (pDiskThread) ? pDiskThread->ActiveStreamCountMax : 0;          return (pDiskThread) ? pDiskThread->ActiveStreamCountMax : 0;
2165      }      }
2166    
2167        int Engine::MaxDiskStreams() {
2168            return iMaxDiskStreams;
2169        }
2170    
2171        void Engine::SetMaxDiskStreams(int iStreams) throw (Exception) {
2172            if (iStreams < 0)
2173                throw Exception("Maximum disk streams for an engine cannot be set lower than 0");
2174    
2175            SuspendAll();
2176    
2177            iMaxDiskStreams = iStreams;
2178    
2179            // reconnect to audio output device, because that will automatically
2180            // recreate the disk thread with the required amount of streams
2181            if (pAudioOutputDevice) Connect(pAudioOutputDevice);
2182    
2183            ResumeAll();
2184        }
2185    
2186      String Engine::DiskStreamBufferFillBytes() {      String Engine::DiskStreamBufferFillBytes() {
2187          return pDiskThread->GetBufferFillBytes();          return pDiskThread->GetBufferFillBytes();
2188      }      }
# Line 1971  namespace LinuxSampler { namespace gig { Line 2196  namespace LinuxSampler { namespace gig {
2196      }      }
2197    
2198      String Engine::Description() {      String Engine::Description() {
2199          return "Gigasampler Engine";          return "Gigasampler Format Engine";
2200      }      }
2201    
2202      String Engine::Version() {      String Engine::Version() {
2203          String s = "$Revision: 1.82 $";          String s = "$Revision: 1.99 $";
2204          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
2205      }      }
2206    
# Line 1984  namespace LinuxSampler { namespace gig { Line 2209  namespace LinuxSampler { namespace gig {
2209      }      }
2210    
2211      // static constant initializers      // static constant initializers
2212      const float* Engine::VolumeCurve(InitVolumeCurve());      const Engine::FloatTable Engine::VolumeCurve(InitVolumeCurve());
2213      const float* Engine::PanCurve(InitPanCurve());      const Engine::FloatTable Engine::PanCurve(InitPanCurve());
2214      const float* Engine::CrossfadeCurve(InitCrossfadeCurve());      const Engine::FloatTable Engine::CrossfadeCurve(InitCrossfadeCurve());
2215    
2216      float* Engine::InitVolumeCurve() {      float* Engine::InitVolumeCurve() {
2217          // line-segment approximation          // line-segment approximation
# Line 2025  namespace LinuxSampler { namespace gig { Line 2250  namespace LinuxSampler { namespace gig {
2250          return y;          return y;
2251      }      }
2252    
     /**  
      * 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;  
     }  
   
2253  }} // namespace LinuxSampler::gig  }} // namespace LinuxSampler::gig

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

  ViewVC Help
Powered by ViewVC