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

Legend:
Removed from v.1555  
changed lines
  Added in v.1893

  ViewVC Help
Powered by ViewVC