/[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 1424 by schoenebeck, Sun Oct 14 22:00:17 2007 UTC revision 1895 by persson, Sun May 3 12:15:40 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 74  namespace LinuxSampler { namespace gig { Line 74  namespace LinuxSampler { namespace gig {
74    
75      /**      /**
76       * Once an engine channel is disconnected from an audio output device,       * Once an engine channel is disconnected from an audio output device,
77       * it wil immediately call this method to unregister itself from the       * it will immediately call this method to unregister itself from the
78       * engine instance and if that engine instance is not used by any other       * engine instance and if that engine instance is not used by any other
79       * engine channel anymore, then that engine instance will be destroyed.       * engine channel anymore, then that engine instance will be destroyed.
80       *       *
# Line 107  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;
116          InstrumentChangeReplyQueue = new RingBuffer<instrument_change_reply_t,false>(1, 0);          FrameTime          = 0;
117    
118          for (RTList<Voice>::Iterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) {          for (RTList<Voice>::Iterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) {
119              iterVoice->SetEngine(this);              iterVoice->SetEngine(this);
# Line 145  namespace LinuxSampler { namespace gig { Line 146  namespace LinuxSampler { namespace gig {
146          if (pVoiceStealingQueue) delete pVoiceStealingQueue;          if (pVoiceStealingQueue) delete pVoiceStealingQueue;
147          if (pSysexBuffer) delete pSysexBuffer;          if (pSysexBuffer) delete pSysexBuffer;
148          if (pGlobalEvents) delete pGlobalEvents;          if (pGlobalEvents) delete pGlobalEvents;
149          if (InstrumentChangeQueue) delete InstrumentChangeQueue;          if (pDimRegionPool[0]) delete pDimRegionPool[0];
150          if (InstrumentChangeReplyQueue) delete InstrumentChangeReplyQueue;          if (pDimRegionPool[1]) delete pDimRegionPool[1];
         if (pDimRegionsInUse) delete[] pDimRegionsInUse;  
151          ResetSuspendedRegions();          ResetSuspendedRegions();
152          Unregister();          Unregister();
153      }      }
# Line 213  namespace LinuxSampler { namespace gig { Line 213  namespace LinuxSampler { namespace gig {
213                      if (hStream != Stream::INVALID_HANDLE) { // voice actually used a stream                      if (hStream != Stream::INVALID_HANDLE) { // voice actually used a stream
214                          iPendingStreamDeletions++;                          iPendingStreamDeletions++;
215                      }                      }
216                        // free the voice to the voice pool and update key info
217                        FreeVoice(pEngineChannel, itVoice);
218                  }                  }
219              }              }
220          }          }
# Line 293  namespace LinuxSampler { namespace gig { Line 295  namespace LinuxSampler { namespace gig {
295          // make sure that the engine does not get any sysex messages          // make sure that the engine does not get any sysex messages
296          // while it's reseting          // while it's reseting
297          bool sysexDisabled = MidiInputPort::RemoveSysexListener(this);          bool sysexDisabled = MidiInputPort::RemoveSysexListener(this);
298          ActiveVoiceCount    = 0;          SetVoiceCount(0);
299          ActiveVoiceCountMax = 0;          ActiveVoiceCountMax = 0;
300    
301          // reset voice stealing parameters          // reset voice stealing parameters
# Line 344  namespace LinuxSampler { namespace gig { Line 346  namespace LinuxSampler { namespace gig {
346       * @param pAudioOut - audio output device to connect to       * @param pAudioOut - audio output device to connect to
347       */       */
348      void Engine::Connect(AudioOutputDevice* pAudioOut) {      void Engine::Connect(AudioOutputDevice* pAudioOut) {
349            // caution: don't ignore if connecting to the same device here,
350            // because otherwise SetMaxDiskStreams() implementation won't work anymore!
351    
352          pAudioOutputDevice = pAudioOut;          pAudioOutputDevice = pAudioOut;
353    
354          ResetInternal();          ResetInternal();
# Line 360  namespace LinuxSampler { namespace gig { Line 365  namespace LinuxSampler { namespace gig {
365          this->MaxSamplesPerCycle = pAudioOutputDevice->MaxSamplesPerCycle();          this->MaxSamplesPerCycle = pAudioOutputDevice->MaxSamplesPerCycle();
366          this->SampleRate         = pAudioOutputDevice->SampleRate();          this->SampleRate         = pAudioOutputDevice->SampleRate();
367    
368          // FIXME: audio drivers with varying fragment sizes might be a problem here          MinFadeOutSamples = int(double(SampleRate) * CONFIG_EG_MIN_RELEASE_TIME) - 1;
369          MaxFadeOutPos = MaxSamplesPerCycle - int(double(SampleRate) * CONFIG_EG_MIN_RELEASE_TIME) - 1;          if (MaxSamplesPerCycle < MinFadeOutSamples) {
         if (MaxFadeOutPos < 0) {  
370              std::cerr << "gig::Engine: WARNING, CONFIG_EG_MIN_RELEASE_TIME "              std::cerr << "gig::Engine: WARNING, CONFIG_EG_MIN_RELEASE_TIME "
371                        << "too big for current audio fragment size & sampling rate! "                        << "too big for current audio fragment size & sampling rate! "
372                        << "May lead to click sounds if voice stealing chimes in!\n" << std::flush;                        << "May lead to click sounds if voice stealing chimes in!\n" << std::flush;
373              // force volume ramp downs at the beginning of each fragment              // force volume ramp downs at the beginning of each fragment
374              MaxFadeOutPos = 0;              MinFadeOutSamples = MaxSamplesPerCycle;
375              // lower minimum release time              // lower minimum release time
376              const float minReleaseTime = (float) MaxSamplesPerCycle / (float) SampleRate;              const float minReleaseTime = (float) MaxSamplesPerCycle / (float) SampleRate;
377              for (RTList<Voice>::Iterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) {              for (RTList<Voice>::Iterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) {
# Line 383  namespace LinuxSampler { namespace gig { Line 387  namespace LinuxSampler { namespace gig {
387              delete this->pDiskThread;              delete this->pDiskThread;
388              dmsg(1,("OK\n"));              dmsg(1,("OK\n"));
389          }          }
390          this->pDiskThread = new DiskThread(((pAudioOut->MaxSamplesPerCycle() << CONFIG_MAX_PITCH) << 1) + 6, //FIXME: assuming stereo          this->pDiskThread =
391                                             &instruments);              new DiskThread(
392                    iMaxDiskStreams,
393                    ((pAudioOut->MaxSamplesPerCycle() << CONFIG_MAX_PITCH) << 1) + 6, //FIXME: assuming stereo
394                    &instruments
395                );
396    
397          if (!pDiskThread) {          if (!pDiskThread) {
398              dmsg(0,("gig::Engine  new diskthread = NULL\n"));              dmsg(0,("gig::Engine  new diskthread = NULL\n"));
399              exit(EXIT_FAILURE);              exit(EXIT_FAILURE);
# Line 410  namespace LinuxSampler { namespace gig { Line 419  namespace LinuxSampler { namespace gig {
419                  exit(EXIT_FAILURE);                  exit(EXIT_FAILURE);
420              }              }
421          }          }
422            pVoicePool->clear();
423      }      }
424    
425      /**      /**
# Line 436  namespace LinuxSampler { namespace gig { Line 446  namespace LinuxSampler { namespace gig {
446                          if (hStream != Stream::INVALID_HANDLE) { // voice actually used a stream                          if (hStream != Stream::INVALID_HANDLE) { // voice actually used a stream
447                              iPendingStreamDeletions++;                              iPendingStreamDeletions++;
448                          }                          }
449                            //NOTE: maybe we should call FreeVoice() here, shouldn't cause a harm though I think, since the voices should be freed by RenderActiveVoices() in the render loop, they are probably just freed a bit later than they could/should be
450                      }                      }
451                  }                  }
452              }              }
# Line 460  namespace LinuxSampler { namespace gig { Line 471  namespace LinuxSampler { namespace gig {
471              // free request slot for next caller (and to make sure that              // free request slot for next caller (and to make sure that
472              // we're not going to process the same request in the next cycle)              // we're not going to process the same request in the next cycle)
473              pPendingRegionSuspension = NULL;              pPendingRegionSuspension = NULL;
474              // if no disk stream deletions are pending, awaker other side, as              // if no disk stream deletions are pending, awaken other side, as
475              // we're done in this case              // we're done in this case
476              if (!iPendingStreamDeletions) SuspensionChangeOngoing.Set(false);              if (!iPendingStreamDeletions) SuspensionChangeOngoing.Set(false);
477          }          }
# Line 571  namespace LinuxSampler { namespace gig { Line 582  namespace LinuxSampler { namespace gig {
582       *  @returns       0 on success       *  @returns       0 on success
583       */       */
584      int Engine::RenderAudio(uint Samples) {      int Engine::RenderAudio(uint Samples) {
585          dmsg(7,("RenderAudio(Samples=%d)\n", Samples));          dmsg(8,("RenderAudio(Samples=%d)\n", Samples));
586    
587          // return if engine disabled          // return if engine disabled
588          if (EngineDisabled.Pop()) {          if (EngineDisabled.Pop()) {
589              dmsg(5,("gig::Engine: engine disabled (val=%d)\n",EngineDisabled.GetUnsafe()));              dmsg(5,("gig::Engine: engine disabled (val=%d)\n",EngineDisabled.GetUnsafe()));
590                EngineDisabled.RttDone();
591              return 0;              return 0;
592          }          }
593    
# Line 586  namespace LinuxSampler { namespace gig { Line 598  namespace LinuxSampler { namespace gig {
598          // update time of start and end of this audio fragment (as events' time stamps relate to this)          // update time of start and end of this audio fragment (as events' time stamps relate to this)
599          pEventGenerator->UpdateFragmentTime(Samples);          pEventGenerator->UpdateFragmentTime(Samples);
600    
601          // We only allow a maximum of CONFIG_MAX_VOICES voices to be spawned          // We only allow the given maximum number of voices to be spawned
602          // in each audio fragment. All subsequent request for spawning new          // in each audio fragment. All subsequent request for spawning new
603          // voices in the same audio fragment will be ignored.          // voices in the same audio fragment will be ignored.
604          VoiceSpawnsLeft = CONFIG_MAX_VOICES;          VoiceSpawnsLeft = MaxVoices();
605    
606          // get all events from the engine's global input event queue which belong to the current fragment          // get all events from the engine's global input event queue which belong to the current fragment
607          // (these are usually just SysEx messages)          // (these are usually just SysEx messages)
# Line 613  namespace LinuxSampler { namespace gig { Line 625  namespace LinuxSampler { namespace gig {
625          ActiveVoiceCountTemp = 0;          ActiveVoiceCountTemp = 0;
626    
627          // handle instrument change commands          // handle instrument change commands
628          instrument_change_command_t command;          bool instrumentChanged = false;
629          if (InstrumentChangeQueue->pop(&command) > 0) {          for (int i = 0; i < engineChannels.size(); i++) {
630              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();  
631    
632              // iterate through all active voices and mark their              // as we're going to (carefully) write some status to the
633              // dimension regions as "in use". The instrument resource              // synchronized struct, we cast away the const
634              // manager may delete all of the instrument except the              EngineChannel::instrument_change_command_t& cmd =
635              // dimension regions and samples that are in use.                  const_cast<EngineChannel::instrument_change_command_t&>(pEngineChannel->InstrumentChangeCommandReader.Lock());
636              int i = 0;  
637              RTList<uint>::Iterator iuiKey = pEngineChannel->pActiveKeys->first();              pEngineChannel->pDimRegionsInUse = cmd.pDimRegionsInUse;
638              RTList<uint>::Iterator end    = pEngineChannel->pActiveKeys->end();              pEngineChannel->pDimRegionsInUse->clear();
639              while (iuiKey != end) { // iterate through all active keys  
640                  midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[*iuiKey];              if (cmd.bChangeInstrument) {
641                  ++iuiKey;                  // change instrument
642                    dmsg(5,("Engine: instrument change command received\n"));
643                    cmd.bChangeInstrument = false;
644                    pEngineChannel->pInstrument = cmd.pInstrument;
645                    instrumentChanged = true;
646    
647                    // Iterate through all active voices and mark them as
648                    // "orphans", which means that the dimension regions
649                    // and samples they use should be released to the
650                    // instrument resource manager when the voices die.
651                    int i = 0;
652                    RTList<uint>::Iterator iuiKey = pEngineChannel->pActiveKeys->first();
653                    RTList<uint>::Iterator end    = pEngineChannel->pActiveKeys->end();
654                    while (iuiKey != end) { // iterate through all active keys
655                        midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[*iuiKey];
656                        ++iuiKey;
657    
658                  RTList<Voice>::Iterator itVoice     = pKey->pActiveVoices->first();                      RTList<Voice>::Iterator itVoice     = pKey->pActiveVoices->first();
659                  RTList<Voice>::Iterator itVoicesEnd = pKey->pActiveVoices->end();                      RTList<Voice>::Iterator itVoicesEnd = pKey->pActiveVoices->end();
660                  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) {  
661                          itVoice->Orphan = true;                          itVoice->Orphan = true;
                         pDimRegionsInUse[i++] = itVoice->pDimRgn;  
662                      }                      }
663                  }                  }
664              }              }
665              pDimRegionsInUse[i] = 0; // end of list          }
666            if (instrumentChanged) {
667              // 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
668              instrument_change_reply_t reply;              ResetSuspendedRegions();
             InstrumentChangeReplyQueue->push(&reply);  
669          }          }
670    
671          // handle events on all engine channels          // handle events on all engine channels
# Line 680  namespace LinuxSampler { namespace gig { Line 700  namespace LinuxSampler { namespace gig {
700          pVoiceStealingQueue->clear();          pVoiceStealingQueue->clear();
701    
702          // just some statistics about this engine instance          // just some statistics about this engine instance
703          ActiveVoiceCount = ActiveVoiceCountTemp;          SetVoiceCount(ActiveVoiceCountTemp);
704          if (ActiveVoiceCount > ActiveVoiceCountMax) ActiveVoiceCountMax = ActiveVoiceCount;          if (VoiceCount() > ActiveVoiceCountMax) ActiveVoiceCountMax = VoiceCount();
705    
706          // in case regions were previously suspended and we killed voices          // in case regions were previously suspended and we killed voices
707          // with disk streams due to that, check if those streams have finally          // with disk streams due to that, check if those streams have finally
708          // been deleted by the disk thread          // been deleted by the disk thread
709          if (iPendingStreamDeletions) ProcessPendingStreamDeletions();          if (iPendingStreamDeletions) ProcessPendingStreamDeletions();
710    
711            for (int i = 0; i < engineChannels.size(); i++) {
712                engineChannels[i]->InstrumentChangeCommandReader.Unlock();
713            }
714          FrameTime += Samples;          FrameTime += Samples;
715    
716            EngineDisabled.RttDone();
717          return 0;          return 0;
718      }      }
719    
# Line 769  namespace LinuxSampler { namespace gig { Line 793  namespace LinuxSampler { namespace gig {
793                  // now render current voice                  // now render current voice
794                  itVoice->Render(Samples);                  itVoice->Render(Samples);
795                  if (itVoice->IsActive()) { // still active                  if (itVoice->IsActive()) { // still active
796                        if (!itVoice->Orphan) {
797                            *(pEngineChannel->pDimRegionsInUse->allocAppend()) = itVoice->pDimRgn;
798                        }
799                      ActiveVoiceCountTemp++;                      ActiveVoiceCountTemp++;
800                      voiceCount++;                      voiceCount++;
801    
802                      if (itVoice->PlaybackState == Voice::playback_state_disk) {                      if (itVoice->PlaybackState == Voice::playback_state_disk) {
803                          if ((itVoice->DiskStreamRef).State == Stream::state_active) streamCount++;                          if ((itVoice->DiskStreamRef).State != Stream::state_unused) streamCount++;
804                      }                      }
805                  }  else { // voice reached end, is now inactive                  }  else { // voice reached end, is now inactive
806                      FreeVoice(pEngineChannel, itVoice); // remove voice from the list of active voices                      FreeVoice(pEngineChannel, itVoice); // remove voice from the list of active voices
# Line 809  namespace LinuxSampler { namespace gig { Line 836  namespace LinuxSampler { namespace gig {
836              if (itNewVoice) {              if (itNewVoice) {
837                  itNewVoice->Render(Samples);                  itNewVoice->Render(Samples);
838                  if (itNewVoice->IsActive()) { // still active                  if (itNewVoice->IsActive()) { // still active
839                        *(pEngineChannel->pDimRegionsInUse->allocAppend()) = itNewVoice->pDimRgn;
840                      ActiveVoiceCountTemp++;                      ActiveVoiceCountTemp++;
841                      pEngineChannel->SetVoiceCount(pEngineChannel->GetVoiceCount() + 1);                      pEngineChannel->SetVoiceCount(pEngineChannel->GetVoiceCount() + 1);
842    
843                      if (itNewVoice->PlaybackState == Voice::playback_state_disk) {                      if (itNewVoice->PlaybackState == Voice::playback_state_disk) {
844                          if (itNewVoice->DiskStreamRef.State == Stream::state_active) {                          if (itNewVoice->DiskStreamRef.State != Stream::state_unused) {
845                              pEngineChannel->SetDiskStreamCount(pEngineChannel->GetDiskStreamCount() + 1);                              pEngineChannel->SetDiskStreamCount(pEngineChannel->GetDiskStreamCount() + 1);
846                          }                          }
847                      }                      }
# Line 844  namespace LinuxSampler { namespace gig { Line 872  namespace LinuxSampler { namespace gig {
872       *                         this audio fragment cycle       *                         this audio fragment cycle
873       */       */
874      void Engine::RouteAudio(EngineChannel* pEngineChannel, uint Samples) {      void Engine::RouteAudio(EngineChannel* pEngineChannel, uint Samples) {
875          // route master signal          // route dry signal
876          {          {
877              AudioChannel* pDstL = pAudioOutputDevice->Channel(pEngineChannel->AudioDeviceChannelLeft);              AudioChannel* pDstL = pAudioOutputDevice->Channel(pEngineChannel->AudioDeviceChannelLeft);
878              AudioChannel* pDstR = pAudioOutputDevice->Channel(pEngineChannel->AudioDeviceChannelRight);              AudioChannel* pDstR = pAudioOutputDevice->Channel(pEngineChannel->AudioDeviceChannelRight);
# Line 855  namespace LinuxSampler { namespace gig { Line 883  namespace LinuxSampler { namespace gig {
883          {          {
884              for (int iFxSend = 0; iFxSend < pEngineChannel->GetFxSendCount(); iFxSend++) {              for (int iFxSend = 0; iFxSend < pEngineChannel->GetFxSendCount(); iFxSend++) {
885                  FxSend* pFxSend = pEngineChannel->GetFxSend(iFxSend);                  FxSend* pFxSend = pEngineChannel->GetFxSend(iFxSend);
886                  // left channel                  for (int iChan = 0; iChan < 2; ++iChan) {
887                  const int iDstL = pFxSend->DestinationChannel(0);                      AudioChannel* pSource =
888                  if (iDstL < 0) {                          (iChan)
889                      dmsg(1,("Engine::RouteAudio() Error: invalid FX send (L) destination channel"));                              ? pEngineChannel->pChannelRight
890                  } else {                              : pEngineChannel->pChannelLeft;
891                      AudioChannel* pDstL = pAudioOutputDevice->Channel(iDstL);                      const int iDstChan = pFxSend->DestinationChannel(iChan);
892                      if (!pDstL) {                      if (iDstChan < 0) {
893                          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));
894                      } else pEngineChannel->pChannelLeft->MixTo(pDstL, Samples, pFxSend->Level());                          goto channel_cleanup;
895                  }                      }
896                  // right channel                      AudioChannel* pDstChan = NULL;
897                  const int iDstR = pFxSend->DestinationChannel(1);                      if (pFxSend->DestinationMasterEffectChain() >= 0) { // fx send routed to an internal master effect
898                  if (iDstR < 0) {                          EffectChain* pEffectChain =
899                      dmsg(1,("Engine::RouteAudio() Error: invalid FX send (R) destination channel"));                              pAudioOutputDevice->MasterEffectChain(
900                  } else {                                  pFxSend->DestinationMasterEffectChain()
901                      AudioChannel* pDstR = pAudioOutputDevice->Channel(iDstR);                              );
902                      if (!pDstR) {                          if (!pEffectChain) {
903                          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()));
904                      } else pEngineChannel->pChannelRight->MixTo(pDstR, Samples, pFxSend->Level());                              goto channel_cleanup;
905                            }
906                            Effect* pEffect =
907                                pEffectChain->GetEffect(
908                                    pFxSend->DestinationMasterEffect()
909                                );
910                            if (!pEffect) {
911                                dmsg(1,("Engine::RouteAudio() Error: invalid FX send (%s) destination effect %d of effect chain %d", ((iChan) ? "R" : "L"), pFxSend->DestinationMasterEffect(), pFxSend->DestinationMasterEffectChain()));
912                                goto channel_cleanup;
913                            }
914                            pDstChan = pEffect->InputChannel(iDstChan);
915                        } else { // FX send routed directly to an audio output channel
916                            pDstChan = pAudioOutputDevice->Channel(iDstChan);
917                        }
918                        if (!pDstChan) {
919                            dmsg(1,("Engine::RouteAudio() Error: invalid FX send (%s) destination channel (%d->%d)", ((iChan) ? "R" : "L"), iChan, iDstChan));
920                            goto channel_cleanup;
921                        }
922                        pSource->MixTo(pDstChan, Samples, pFxSend->Level());
923                  }                  }
924              }              }
925          }          }
926            channel_cleanup:
927          // reset buffers with silence (zero out) for the next audio cycle          // reset buffers with silence (zero out) for the next audio cycle
928          pEngineChannel->pChannelLeft->Clear();          pEngineChannel->pChannelLeft->Clear();
929          pEngineChannel->pChannelRight->Clear();          pEngineChannel->pChannelRight->Clear();
# Line 922  namespace LinuxSampler { namespace gig { Line 969  namespace LinuxSampler { namespace gig {
969       *       *
970       *  @param pData - pointer to sysex data       *  @param pData - pointer to sysex data
971       *  @param Size  - lenght of sysex data (in bytes)       *  @param Size  - lenght of sysex data (in bytes)
972         *  @param pSender - the MIDI input port on which the SysEx message was
973         *                   received
974       */       */
975      void Engine::SendSysex(void* pData, uint Size) {      void Engine::SendSysex(void* pData, uint Size, MidiInputPort* pSender) {
976          Event event             = pEventGenerator->CreateEvent();          Event event             = pEventGenerator->CreateEvent();
977          event.Type              = Event::type_sysex;          event.Type              = Event::type_sysex;
978          event.Param.Sysex.Size  = Size;          event.Param.Sysex.Size  = Size;
979          event.pEngineChannel    = NULL; // as Engine global event          event.pEngineChannel    = NULL; // as Engine global event
980            event.pMidiInputPort    = pSender;
981          if (pEventQueue->write_space() > 0) {          if (pEventQueue->write_space() > 0) {
982              if (pSysexBuffer->write_space() >= Size) {              if (pSysexBuffer->write_space() >= Size) {
983                  // copy sysex data to input buffer                  // copy sysex data to input buffer
# Line 1605  namespace LinuxSampler { namespace gig { Line 1655  namespace LinuxSampler { namespace gig {
1655      void Engine::ProcessControlChange(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itControlChangeEvent) {      void Engine::ProcessControlChange(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itControlChangeEvent) {
1656          dmsg(4,("Engine::ContinuousController cc=%d v=%d\n", itControlChangeEvent->Param.CC.Controller, itControlChangeEvent->Param.CC.Value));          dmsg(4,("Engine::ContinuousController cc=%d v=%d\n", itControlChangeEvent->Param.CC.Controller, itControlChangeEvent->Param.CC.Value));
1657    
1658            // handle the "control triggered" MIDI rule: a control change
1659            // event can trigger a new note on or note off event
1660            if (pEngineChannel->pInstrument) {
1661    
1662                ::gig::MidiRule* rule;
1663                for (int i = 0 ; (rule = pEngineChannel->pInstrument->GetMidiRule(i)) ; i++) {
1664    
1665                    if (::gig::MidiRuleCtrlTrigger* ctrlTrigger =
1666                        dynamic_cast< ::gig::MidiRuleCtrlTrigger*>(rule)) {
1667                        if (itControlChangeEvent->Param.CC.Controller ==
1668                            ctrlTrigger->ControllerNumber) {
1669    
1670                            uint8_t oldCCValue = pEngineChannel->ControllerTable[
1671                                itControlChangeEvent->Param.CC.Controller];
1672                            uint8_t newCCValue = itControlChangeEvent->Param.CC.Value;
1673    
1674                            for (int i = 0 ; i < ctrlTrigger->Triggers ; i++) {
1675                                ::gig::MidiRuleCtrlTrigger::trigger_t* pTrigger =
1676                                      &ctrlTrigger->pTriggers[i];
1677    
1678                                // check if the controller has passed the
1679                                // trigger point in the right direction
1680                                if ((pTrigger->Descending &&
1681                                     oldCCValue > pTrigger->TriggerPoint &&
1682                                     newCCValue <= pTrigger->TriggerPoint) ||
1683                                    (!pTrigger->Descending &&
1684                                     oldCCValue < pTrigger->TriggerPoint &&
1685                                     newCCValue >= pTrigger->TriggerPoint)) {
1686    
1687                                    RTList<Event>::Iterator itNewEvent = pGlobalEvents->allocAppend();
1688                                    if (itNewEvent) {
1689                                        *itNewEvent = *itControlChangeEvent;
1690                                        itNewEvent->Param.Note.Key = pTrigger->Key;
1691    
1692                                        if (pTrigger->NoteOff || pTrigger->Velocity == 0) {
1693                                            itNewEvent->Type = Event::type_note_off;
1694                                            itNewEvent->Param.Note.Velocity = 100;
1695    
1696                                            ProcessNoteOff(pEngineChannel, itNewEvent);
1697                                        } else {
1698                                            itNewEvent->Type = Event::type_note_on;
1699                                            //TODO: if Velocity is 255, the triggered velocity should
1700                                            // depend on how fast the controller is moving
1701                                            itNewEvent->Param.Note.Velocity =
1702                                                pTrigger->Velocity == 255 ? 100 :
1703                                                pTrigger->Velocity;
1704    
1705                                            ProcessNoteOn(pEngineChannel, itNewEvent);
1706                                        }
1707                                    }
1708                                    else dmsg(1,("Event pool emtpy!\n"));
1709                                }
1710                            }
1711                        }
1712                    }
1713                }
1714            }
1715    
1716          // update controller value in the engine channel's controller table          // update controller value in the engine channel's controller table
1717          pEngineChannel->ControllerTable[itControlChangeEvent->Param.CC.Controller] = itControlChangeEvent->Param.CC.Value;          pEngineChannel->ControllerTable[itControlChangeEvent->Param.CC.Controller] = itControlChangeEvent->Param.CC.Value;
1718    
# Line 1638  namespace LinuxSampler { namespace gig { Line 1746  namespace LinuxSampler { namespace gig {
1746                  //TODO: not sample accurate yet                  //TODO: not sample accurate yet
1747                  pEngineChannel->GlobalPanLeft  = PanCurve[128 - itControlChangeEvent->Param.CC.Value];                  pEngineChannel->GlobalPanLeft  = PanCurve[128 - itControlChangeEvent->Param.CC.Value];
1748                  pEngineChannel->GlobalPanRight = PanCurve[itControlChangeEvent->Param.CC.Value];                  pEngineChannel->GlobalPanRight = PanCurve[itControlChangeEvent->Param.CC.Value];
1749                    pEngineChannel->iLastPanRequest = itControlChangeEvent->Param.CC.Value;
1750                  break;                  break;
1751              }              }
1752              case 64: { // sustain              case 64: { // sustain
# Line 1778  namespace LinuxSampler { namespace gig { Line 1887  namespace LinuxSampler { namespace gig {
1887          if (!pEngineChannel->fxSends.empty()) {          if (!pEngineChannel->fxSends.empty()) {
1888              for (int iFxSend = 0; iFxSend < pEngineChannel->GetFxSendCount(); iFxSend++) {              for (int iFxSend = 0; iFxSend < pEngineChannel->GetFxSendCount(); iFxSend++) {
1889                  FxSend* pFxSend = pEngineChannel->GetFxSend(iFxSend);                  FxSend* pFxSend = pEngineChannel->GetFxSend(iFxSend);
1890                  if (pFxSend->MidiController() == itControlChangeEvent->Param.CC.Controller)                  if (pFxSend->MidiController() == itControlChangeEvent->Param.CC.Controller) {
1891                      pFxSend->SetLevel(itControlChangeEvent->Param.CC.Value);                      pFxSend->SetLevel(itControlChangeEvent->Param.CC.Value);
1892                      pFxSend->SetInfoChanged(true);                      pFxSend->SetInfoChanged(true);
1893                    }
1894              }              }
1895          }          }
1896      }      }
# Line 1799  namespace LinuxSampler { namespace gig { Line 1909  namespace LinuxSampler { namespace gig {
1909          if (exclusive_status != 0xF0)       goto free_sysex_data;          if (exclusive_status != 0xF0)       goto free_sysex_data;
1910    
1911          switch (id) {          switch (id) {
1912                case 0x7f: { // (Realtime) Universal Sysex (GM Standard)
1913                    uint8_t sysex_channel, sub_id1, sub_id2, val_msb, val_lsb;;
1914                    if (!reader.pop(&sysex_channel)) goto free_sysex_data;
1915                    if (!reader.pop(&sub_id1)) goto free_sysex_data;
1916                    if (!reader.pop(&sub_id2)) goto free_sysex_data;
1917                    if (!reader.pop(&val_lsb)) goto free_sysex_data;
1918                    if (!reader.pop(&val_msb)) goto free_sysex_data;
1919                    //TODO: for now we simply ignore the sysex channel, seldom used anyway
1920                    switch (sub_id1) {
1921                        case 0x04: // Device Control
1922                            switch (sub_id2) {
1923                                case 0x01: { // Master Volume
1924                                    const double volume =
1925                                        double((uint(val_msb)<<7) | uint(val_lsb)) / 16383.0;
1926                                    #if CONFIG_MASTER_VOLUME_SYSEX_BY_PORT
1927                                    // apply volume to all sampler channels that
1928                                    // are connected to the same MIDI input port
1929                                    // this sysex message arrived on
1930                                    for (int i = 0; i < engineChannels.size(); ++i) {
1931                                        EngineChannel* pEngineChannel = engineChannels[i];
1932                                        if (pEngineChannel->GetMidiInputPort() ==
1933                                            itSysexEvent->pMidiInputPort)
1934                                        {
1935                                            pEngineChannel->Volume(volume);
1936                                        }
1937                                    }
1938                                    #else
1939                                    // apply volume globally to the whole sampler
1940                                    GLOBAL_VOLUME = volume;
1941                                    #endif // CONFIG_MASTER_VOLUME_SYSEX_BY_PORT
1942                                    break;
1943                                }
1944                            }
1945                            break;
1946                    }
1947                    break;
1948                }
1949              case 0x41: { // Roland              case 0x41: { // Roland
1950                  dmsg(3,("Roland Sysex\n"));                  dmsg(3,("Roland Sysex\n"));
1951                  uint8_t device_id, model_id, cmd_id;                  uint8_t device_id, model_id, cmd_id;
# Line 1814  namespace LinuxSampler { namespace gig { Line 1961  namespace LinuxSampler { namespace gig {
1961                  if (reader.read(&addr[0], 3) != 3) goto free_sysex_data;                  if (reader.read(&addr[0], 3) != 3) goto free_sysex_data;
1962                  if (addr[0] == 0x40 && addr[1] == 0x00) { // System Parameters                  if (addr[0] == 0x40 && addr[1] == 0x00) { // System Parameters
1963                      dmsg(3,("\tSystem Parameter\n"));                      dmsg(3,("\tSystem Parameter\n"));
1964                        if (addr[2] == 0x7f) { // GS reset
1965                            for (int i = 0; i < engineChannels.size(); ++i) {
1966                                EngineChannel* pEngineChannel = engineChannels[i];
1967                                if (pEngineChannel->GetMidiInputPort() == itSysexEvent->pMidiInputPort) {
1968                                    KillAllVoices(pEngineChannel, itSysexEvent);
1969                                    pEngineChannel->ResetControllers();
1970                                }
1971                            }
1972                        }
1973                  }                  }
1974                  else if (addr[0] == 0x40 && addr[1] == 0x01) { // Common Parameters                  else if (addr[0] == 0x40 && addr[1] == 0x01) { // Common Parameters
1975                      dmsg(3,("\tCommon Parameter\n"));                      dmsg(3,("\tCommon Parameter\n"));
# Line 1835  namespace LinuxSampler { namespace gig { Line 1991  namespace LinuxSampler { namespace gig {
1991                              dmsg(3,("\t\t\tNew scale applied.\n"));                              dmsg(3,("\t\t\tNew scale applied.\n"));
1992                              break;                              break;
1993                          }                          }
1994                            case 0x15: { // chromatic / drumkit mode
1995                                dmsg(3,("\t\tMIDI Instrument Map Switch\n"));
1996                                uint8_t part = addr[1] & 0x0f;
1997                                uint8_t map;
1998                                if (!reader.pop(&map)) goto free_sysex_data;
1999                                for (int i = 0; i < engineChannels.size(); ++i) {
2000                                    EngineChannel* pEngineChannel = engineChannels[i];
2001                                    if (
2002                                        (pEngineChannel->midiChannel == part ||
2003                                         pEngineChannel->midiChannel == midi_chan_all) &&
2004                                         pEngineChannel->GetMidiInputPort() == itSysexEvent->pMidiInputPort
2005                                    ) {
2006                                        try {
2007                                            pEngineChannel->SetMidiInstrumentMap(map);
2008                                        } catch (Exception e) {
2009                                            dmsg(2,("\t\t\tCould not apply MIDI instrument map %d to part %d: %s\n", map, part, e.Message().c_str()));
2010                                            goto free_sysex_data;
2011                                        } catch (...) {
2012                                            dmsg(2,("\t\t\tCould not apply MIDI instrument map %d to part %d (unknown exception)\n", map, part));
2013                                            goto free_sysex_data;
2014                                        }
2015                                    }
2016                                }
2017                                dmsg(3,("\t\t\tApplied MIDI instrument map %d to part %d.\n", map, part));
2018                                break;
2019                            }
2020                      }                      }
2021                  }                  }
2022                  else if (addr[0] == 0x40 && (addr[1] & 0xf0) == 0x20) { // Part Parameters (2)                  else if (addr[0] == 0x40 && (addr[1] & 0xf0) == 0x20) { // Part Parameters (2)
# Line 1941  namespace LinuxSampler { namespace gig { Line 2123  namespace LinuxSampler { namespace gig {
2123      }      }
2124    
2125      uint Engine::VoiceCount() {      uint Engine::VoiceCount() {
2126          return ActiveVoiceCount;          return atomic_read(&ActiveVoiceCount);
2127        }
2128    
2129        void Engine::SetVoiceCount(uint Count) {
2130            atomic_set(&ActiveVoiceCount, Count);
2131      }      }
2132    
2133      uint Engine::VoiceCountMax() {      uint Engine::VoiceCountMax() {
2134          return ActiveVoiceCountMax;          return ActiveVoiceCountMax;
2135      }      }
2136    
2137        int Engine::MaxVoices() {
2138            return pVoicePool->poolSize();
2139        }
2140    
2141        void Engine::SetMaxVoices(int iVoices) throw (Exception) {
2142            if (iVoices < 1)
2143                throw Exception("Maximum voices for an engine cannot be set lower than 1");
2144    
2145            SuspendAll();
2146    
2147            // NOTE: we need to clear pDimRegionsInUse before deleting pDimRegionPool,
2148            // otherwise memory corruption will occur if there are active voices (see bug #118)
2149            for (int iChannel = 0; iChannel < engineChannels.size(); iChannel++) {
2150                engineChannels[iChannel]->ClearDimRegionsInUse();
2151            }
2152    
2153            if (pDimRegionPool[0]) delete pDimRegionPool[0];
2154            if (pDimRegionPool[1]) delete pDimRegionPool[1];
2155    
2156            pDimRegionPool[0] = new Pool< ::gig::DimensionRegion*>(iVoices);
2157            pDimRegionPool[1] = new Pool< ::gig::DimensionRegion*>(iVoices);
2158    
2159            for (int iChannel = 0; iChannel < engineChannels.size(); iChannel++) {
2160                engineChannels[iChannel]->ResetDimRegionsInUse();
2161            }
2162    
2163            try {
2164                pVoicePool->resizePool(iVoices);
2165            } catch (...) {
2166                throw Exception("FATAL: Could not resize voice pool!");
2167            }
2168    
2169            for (RTList<Voice>::Iterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) {
2170                iterVoice->SetEngine(this);
2171                iterVoice->pDiskThread = this->pDiskThread;
2172            }
2173            pVoicePool->clear();
2174    
2175            ResumeAll();
2176        }
2177    
2178      bool Engine::DiskStreamSupported() {      bool Engine::DiskStreamSupported() {
2179          return true;          return true;
2180      }      }
2181    
2182      uint Engine::DiskStreamCount() {      uint Engine::DiskStreamCount() {
2183          return (pDiskThread) ? pDiskThread->ActiveStreamCount : 0;          return (pDiskThread) ? pDiskThread->GetActiveStreamCount() : 0;
2184      }      }
2185    
2186      uint Engine::DiskStreamCountMax() {      uint Engine::DiskStreamCountMax() {
2187          return (pDiskThread) ? pDiskThread->ActiveStreamCountMax : 0;          return (pDiskThread) ? pDiskThread->ActiveStreamCountMax : 0;
2188      }      }
2189    
2190        int Engine::MaxDiskStreams() {
2191            return iMaxDiskStreams;
2192        }
2193    
2194        void Engine::SetMaxDiskStreams(int iStreams) throw (Exception) {
2195            if (iStreams < 0)
2196                throw Exception("Maximum disk streams for an engine cannot be set lower than 0");
2197    
2198            SuspendAll();
2199    
2200            iMaxDiskStreams = iStreams;
2201    
2202            // reconnect to audio output device, because that will automatically
2203            // recreate the disk thread with the required amount of streams
2204            if (pAudioOutputDevice) Connect(pAudioOutputDevice);
2205    
2206            ResumeAll();
2207        }
2208    
2209      String Engine::DiskStreamBufferFillBytes() {      String Engine::DiskStreamBufferFillBytes() {
2210          return pDiskThread->GetBufferFillBytes();          return pDiskThread->GetBufferFillBytes();
2211      }      }
# Line 1973  namespace LinuxSampler { namespace gig { Line 2219  namespace LinuxSampler { namespace gig {
2219      }      }
2220    
2221      String Engine::Description() {      String Engine::Description() {
2222          return "Gigasampler Engine";          return "Gigasampler Format Engine";
2223      }      }
2224    
2225      String Engine::Version() {      String Engine::Version() {
2226          String s = "$Revision: 1.83 $";          String s = "$Revision: 1.103 $";
2227          return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword          return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword
2228      }      }
2229    
# Line 1986  namespace LinuxSampler { namespace gig { Line 2232  namespace LinuxSampler { namespace gig {
2232      }      }
2233    
2234      // static constant initializers      // static constant initializers
2235      const float* Engine::VolumeCurve(InitVolumeCurve());      const Engine::FloatTable Engine::VolumeCurve(InitVolumeCurve());
2236      const float* Engine::PanCurve(InitPanCurve());      const Engine::FloatTable Engine::PanCurve(InitPanCurve());
2237      const float* Engine::CrossfadeCurve(InitCrossfadeCurve());      const Engine::FloatTable Engine::CrossfadeCurve(InitCrossfadeCurve());
2238    
2239      float* Engine::InitVolumeCurve() {      float* Engine::InitVolumeCurve() {
2240          // line-segment approximation          // line-segment approximation
# Line 2027  namespace LinuxSampler { namespace gig { Line 2273  namespace LinuxSampler { namespace gig {
2273          return y;          return y;
2274      }      }
2275    
     /**  
      * 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;  
     }  
   
2276  }} // namespace LinuxSampler::gig  }} // namespace LinuxSampler::gig

Legend:
Removed from v.1424  
changed lines
  Added in v.1895

  ViewVC Help
Powered by ViewVC