/[svn]/linuxsampler/trunk/src/engines/gig/Engine.cpp
ViewVC logotype

Diff of /linuxsampler/trunk/src/engines/gig/Engine.cpp

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1399 by schoenebeck, Thu Oct 11 18:53:29 2007 UTC revision 1700 by persson, Sun Feb 17 12:40:59 2008 UTC
# Line 2  Line 2 
2   *                                                                         *   *                                                                         *
3   *   LinuxSampler - modular, streaming capable sampler                     *   *   LinuxSampler - modular, streaming capable sampler                     *
4   *                                                                         *   *                                                                         *
5   *   Copyright (C) 2003,2004 by Benno Senoner and Christian Schoenebeck   *   *   Copyright (C) 2003,2004 by Benno Senoner and Christian Schoenebeck    *
6   *   Copyright (C) 2005-2007 Christian Schoenebeck                        *   *   Copyright (C) 2005-2008 Christian Schoenebeck                         *
7   *                                                                         *   *                                                                         *
8   *   This program is free software; you can redistribute it and/or modify  *   *   This program is free software; you can redistribute it and/or modify  *
9   *   it under the terms of the GNU General Public License as published by  *   *   it under the terms of the GNU General Public License as published by  *
# Line 29  Line 29 
29    
30  #include "Engine.h"  #include "Engine.h"
31    
32    #include "../../common/global_private.h"
33    
34  namespace LinuxSampler { namespace gig {  namespace LinuxSampler { namespace gig {
35    
36      InstrumentResourceManager Engine::instruments;      InstrumentResourceManager Engine::instruments;
# Line 72  namespace LinuxSampler { namespace gig { Line 74  namespace LinuxSampler { namespace gig {
74    
75      /**      /**
76       * Once an engine channel is disconnected from an audio output device,       * Once an engine channel is disconnected from an audio output device,
77       * it wil immediately call this method to unregister itself from the       * it will immediately call this method to unregister itself from the
78       * engine instance and if that engine instance is not used by any other       * engine instance and if that engine instance is not used by any other
79       * engine channel anymore, then that engine instance will be destroyed.       * engine channel anymore, then that engine instance will be destroyed.
80       *       *
# Line 106  namespace LinuxSampler { namespace gig { Line 108  namespace LinuxSampler { namespace gig {
108          pEventQueue        = new RingBuffer<Event,false>(CONFIG_MAX_EVENTS_PER_FRAGMENT, 0);          pEventQueue        = new RingBuffer<Event,false>(CONFIG_MAX_EVENTS_PER_FRAGMENT, 0);
109          pEventPool         = new Pool<Event>(CONFIG_MAX_EVENTS_PER_FRAGMENT);          pEventPool         = new Pool<Event>(CONFIG_MAX_EVENTS_PER_FRAGMENT);
110          pVoicePool         = new Pool<Voice>(CONFIG_MAX_VOICES);          pVoicePool         = new Pool<Voice>(CONFIG_MAX_VOICES);
111          pDimRegionsInUse   = new ::gig::DimensionRegion*[CONFIG_MAX_VOICES + 1];          pDimRegionPool[0]  = new Pool< ::gig::DimensionRegion*>(CONFIG_MAX_VOICES);
112            pDimRegionPool[1]  = new Pool< ::gig::DimensionRegion*>(CONFIG_MAX_VOICES);
113          pVoiceStealingQueue = new RTList<Event>(pEventPool);          pVoiceStealingQueue = new RTList<Event>(pEventPool);
114          pGlobalEvents      = new RTList<Event>(pEventPool);          pGlobalEvents      = new RTList<Event>(pEventPool);
         InstrumentChangeQueue      = new RingBuffer<instrument_change_command_t,false>(1, 0);  
         InstrumentChangeReplyQueue = new RingBuffer<instrument_change_reply_t,false>(1, 0);  
115    
116          for (RTList<Voice>::Iterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) {          for (RTList<Voice>::Iterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) {
117              iterVoice->SetEngine(this);              iterVoice->SetEngine(this);
# Line 143  namespace LinuxSampler { namespace gig { Line 144  namespace LinuxSampler { namespace gig {
144          if (pVoiceStealingQueue) delete pVoiceStealingQueue;          if (pVoiceStealingQueue) delete pVoiceStealingQueue;
145          if (pSysexBuffer) delete pSysexBuffer;          if (pSysexBuffer) delete pSysexBuffer;
146          if (pGlobalEvents) delete pGlobalEvents;          if (pGlobalEvents) delete pGlobalEvents;
147          if (InstrumentChangeQueue) delete InstrumentChangeQueue;          if (pDimRegionPool[0]) delete pDimRegionPool[0];
148          if (InstrumentChangeReplyQueue) delete InstrumentChangeReplyQueue;          if (pDimRegionPool[1]) delete pDimRegionPool[1];
         if (pDimRegionsInUse) delete[] pDimRegionsInUse;  
149          ResetSuspendedRegions();          ResetSuspendedRegions();
150          Unregister();          Unregister();
151      }      }
# Line 458  namespace LinuxSampler { namespace gig { Line 458  namespace LinuxSampler { namespace gig {
458              // free request slot for next caller (and to make sure that              // free request slot for next caller (and to make sure that
459              // we're not going to process the same request in the next cycle)              // we're not going to process the same request in the next cycle)
460              pPendingRegionSuspension = NULL;              pPendingRegionSuspension = NULL;
461              // if no disk stream deletions are pending, awaker other side, as              // if no disk stream deletions are pending, awaken other side, as
462              // we're done in this case              // we're done in this case
463              if (!iPendingStreamDeletions) SuspensionChangeOngoing.Set(false);              if (!iPendingStreamDeletions) SuspensionChangeOngoing.Set(false);
464          }          }
# Line 569  namespace LinuxSampler { namespace gig { Line 569  namespace LinuxSampler { namespace gig {
569       *  @returns       0 on success       *  @returns       0 on success
570       */       */
571      int Engine::RenderAudio(uint Samples) {      int Engine::RenderAudio(uint Samples) {
572          dmsg(7,("RenderAudio(Samples=%d)\n", Samples));          dmsg(8,("RenderAudio(Samples=%d)\n", Samples));
573    
574          // return if engine disabled          // return if engine disabled
575          if (EngineDisabled.Pop()) {          if (EngineDisabled.Pop()) {
# Line 611  namespace LinuxSampler { namespace gig { Line 611  namespace LinuxSampler { namespace gig {
611          ActiveVoiceCountTemp = 0;          ActiveVoiceCountTemp = 0;
612    
613          // handle instrument change commands          // handle instrument change commands
614          instrument_change_command_t command;          bool instrumentChanged = false;
615          if (InstrumentChangeQueue->pop(&command) > 0) {          for (int i = 0; i < engineChannels.size(); i++) {
616              EngineChannel* pEngineChannel = command.pEngineChannel;              EngineChannel* pEngineChannel = engineChannels[i];
             pEngineChannel->pInstrument = command.pInstrument;  
   
             //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();  
617    
618              // iterate through all active voices and mark their              // as we're going to (carefully) write some status to the
619              // dimension regions as "in use". The instrument resource              // synchronized struct, we cast away the const
620              // manager may delete all of the instrument except the              EngineChannel::instrument_change_command_t& cmd =
621              // dimension regions and samples that are in use.                  const_cast<EngineChannel::instrument_change_command_t&>(pEngineChannel->InstrumentChangeCommandReader.Lock());
622              int i = 0;  
623              RTList<uint>::Iterator iuiKey = pEngineChannel->pActiveKeys->first();              pEngineChannel->pDimRegionsInUse = cmd.pDimRegionsInUse;
624              RTList<uint>::Iterator end    = pEngineChannel->pActiveKeys->end();              pEngineChannel->pDimRegionsInUse->clear();
625              while (iuiKey != end) { // iterate through all active keys  
626                  midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[*iuiKey];              if (cmd.bChangeInstrument) {
627                  ++iuiKey;                  // change instrument
628                    dmsg(5,("Engine: instrument change command received\n"));
629                    cmd.bChangeInstrument = false;
630                    pEngineChannel->pInstrument = cmd.pInstrument;
631                    instrumentChanged = true;
632    
633                    // Iterate through all active voices and mark them as
634                    // "orphans", which means that the dimension regions
635                    // and samples they use should be released to the
636                    // instrument resource manager when the voices die.
637                    int i = 0;
638                    RTList<uint>::Iterator iuiKey = pEngineChannel->pActiveKeys->first();
639                    RTList<uint>::Iterator end    = pEngineChannel->pActiveKeys->end();
640                    while (iuiKey != end) { // iterate through all active keys
641                        midi_key_info_t* pKey = &pEngineChannel->pMIDIKeyInfo[*iuiKey];
642                        ++iuiKey;
643    
644                  RTList<Voice>::Iterator itVoice     = pKey->pActiveVoices->first();                      RTList<Voice>::Iterator itVoice     = pKey->pActiveVoices->first();
645                  RTList<Voice>::Iterator itVoicesEnd = pKey->pActiveVoices->end();                      RTList<Voice>::Iterator itVoicesEnd = pKey->pActiveVoices->end();
646                  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) {  
647                          itVoice->Orphan = true;                          itVoice->Orphan = true;
                         pDimRegionsInUse[i++] = itVoice->pDimRgn;  
648                      }                      }
649                  }                  }
650              }              }
651              pDimRegionsInUse[i] = 0; // end of list          }
652            if (instrumentChanged) {
653              // send a reply to the calling thread, which is waiting              //TODO: this is a lazy solution ATM and not safe in case somebody is currently editing the instrument we're currently switching to (we should store all suspended regions on instrument manager side and when switching to another instrument copy that list to the engine's local list of suspensions
654              instrument_change_reply_t reply;              ResetSuspendedRegions();
             InstrumentChangeReplyQueue->push(&reply);  
655          }          }
656    
657          // handle events on all engine channels          // handle events on all engine channels
# Line 686  namespace LinuxSampler { namespace gig { Line 694  namespace LinuxSampler { namespace gig {
694          // been deleted by the disk thread          // been deleted by the disk thread
695          if (iPendingStreamDeletions) ProcessPendingStreamDeletions();          if (iPendingStreamDeletions) ProcessPendingStreamDeletions();
696    
697            for (int i = 0; i < engineChannels.size(); i++) {
698                engineChannels[i]->InstrumentChangeCommandReader.Unlock();
699            }
700          FrameTime += Samples;          FrameTime += Samples;
701    
702          return 0;          return 0;
# Line 767  namespace LinuxSampler { namespace gig { Line 778  namespace LinuxSampler { namespace gig {
778                  // now render current voice                  // now render current voice
779                  itVoice->Render(Samples);                  itVoice->Render(Samples);
780                  if (itVoice->IsActive()) { // still active                  if (itVoice->IsActive()) { // still active
781                        if (!itVoice->Orphan) {
782                            *(pEngineChannel->pDimRegionsInUse->allocAppend()) = itVoice->pDimRgn;
783                        }
784                      ActiveVoiceCountTemp++;                      ActiveVoiceCountTemp++;
785                      voiceCount++;                      voiceCount++;
786    
# Line 807  namespace LinuxSampler { namespace gig { Line 821  namespace LinuxSampler { namespace gig {
821              if (itNewVoice) {              if (itNewVoice) {
822                  itNewVoice->Render(Samples);                  itNewVoice->Render(Samples);
823                  if (itNewVoice->IsActive()) { // still active                  if (itNewVoice->IsActive()) { // still active
824                        *(pEngineChannel->pDimRegionsInUse->allocAppend()) = itNewVoice->pDimRgn;
825                      ActiveVoiceCountTemp++;                      ActiveVoiceCountTemp++;
826                      pEngineChannel->SetVoiceCount(pEngineChannel->GetVoiceCount() + 1);                      pEngineChannel->SetVoiceCount(pEngineChannel->GetVoiceCount() + 1);
827    
# Line 1603  namespace LinuxSampler { namespace gig { Line 1618  namespace LinuxSampler { namespace gig {
1618      void Engine::ProcessControlChange(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itControlChangeEvent) {      void Engine::ProcessControlChange(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itControlChangeEvent) {
1619          dmsg(4,("Engine::ContinuousController cc=%d v=%d\n", itControlChangeEvent->Param.CC.Controller, itControlChangeEvent->Param.CC.Value));          dmsg(4,("Engine::ContinuousController cc=%d v=%d\n", itControlChangeEvent->Param.CC.Controller, itControlChangeEvent->Param.CC.Value));
1620    
1621            // handle the "control triggered" MIDI rule: a control change
1622            // event can trigger a new note on or note off event
1623            if (pEngineChannel->pInstrument) {
1624    
1625                ::gig::MidiRule* rule;
1626                for (int i = 0 ; (rule = pEngineChannel->pInstrument->GetMidiRule(i)) ; i++) {
1627    
1628                    if (::gig::MidiRuleCtrlTrigger* ctrlTrigger =
1629                        dynamic_cast< ::gig::MidiRuleCtrlTrigger*>(rule)) {
1630                        if (itControlChangeEvent->Param.CC.Controller ==
1631                            ctrlTrigger->ControllerNumber) {
1632    
1633                            uint8_t oldCCValue = pEngineChannel->ControllerTable[
1634                                itControlChangeEvent->Param.CC.Controller];
1635                            uint8_t newCCValue = itControlChangeEvent->Param.CC.Value;
1636    
1637                            for (int i = 0 ; i < ctrlTrigger->Triggers ; i++) {
1638                                ::gig::MidiRuleCtrlTrigger::trigger_t* pTrigger =
1639                                      &ctrlTrigger->pTriggers[i];
1640    
1641                                // check if the controller has passed the
1642                                // trigger point in the right direction
1643                                if ((pTrigger->Descending &&
1644                                     oldCCValue > pTrigger->TriggerPoint &&
1645                                     newCCValue <= pTrigger->TriggerPoint) ||
1646                                    (!pTrigger->Descending &&
1647                                     oldCCValue < pTrigger->TriggerPoint &&
1648                                     newCCValue >= pTrigger->TriggerPoint)) {
1649    
1650                                    RTList<Event>::Iterator itNewEvent = pGlobalEvents->allocAppend();
1651                                    if (itNewEvent) {
1652                                        *itNewEvent = *itControlChangeEvent;
1653                                        itNewEvent->Param.Note.Key = pTrigger->Key;
1654    
1655                                        if (pTrigger->NoteOff || pTrigger->Velocity == 0) {
1656                                            itNewEvent->Type = Event::type_note_off;
1657                                            itNewEvent->Param.Note.Velocity = 100;
1658    
1659                                            ProcessNoteOff(pEngineChannel, itNewEvent);
1660                                        } else {
1661                                            itNewEvent->Type = Event::type_note_on;
1662                                            //TODO: if Velocity is 255, the triggered velocity should
1663                                            // depend on how fast the controller is moving
1664                                            itNewEvent->Param.Note.Velocity =
1665                                                pTrigger->Velocity == 255 ? 100 :
1666                                                pTrigger->Velocity;
1667    
1668                                            ProcessNoteOn(pEngineChannel, itNewEvent);
1669                                        }
1670                                    }
1671                                    else dmsg(1,("Event pool emtpy!\n"));
1672                                }
1673                            }
1674                        }
1675                    }
1676                }
1677            }
1678    
1679          // update controller value in the engine channel's controller table          // update controller value in the engine channel's controller table
1680          pEngineChannel->ControllerTable[itControlChangeEvent->Param.CC.Controller] = itControlChangeEvent->Param.CC.Value;          pEngineChannel->ControllerTable[itControlChangeEvent->Param.CC.Controller] = itControlChangeEvent->Param.CC.Value;
1681    
# Line 1971  namespace LinuxSampler { namespace gig { Line 2044  namespace LinuxSampler { namespace gig {
2044      }      }
2045    
2046      String Engine::Description() {      String Engine::Description() {
2047          return "Gigasampler Engine";          return "Gigasampler Format Engine";
2048      }      }
2049    
2050      String Engine::Version() {      String Engine::Version() {
2051          String s = "$Revision: 1.82 $";          String s = "$Revision: 1.88 $";
2052          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
2053      }      }
2054    
# Line 1984  namespace LinuxSampler { namespace gig { Line 2057  namespace LinuxSampler { namespace gig {
2057      }      }
2058    
2059      // static constant initializers      // static constant initializers
2060      const float* Engine::VolumeCurve(InitVolumeCurve());      const Engine::FloatTable Engine::VolumeCurve(InitVolumeCurve());
2061      const float* Engine::PanCurve(InitPanCurve());      const Engine::FloatTable Engine::PanCurve(InitPanCurve());
2062      const float* Engine::CrossfadeCurve(InitCrossfadeCurve());      const Engine::FloatTable Engine::CrossfadeCurve(InitCrossfadeCurve());
2063    
2064      float* Engine::InitVolumeCurve() {      float* Engine::InitVolumeCurve() {
2065          // line-segment approximation          // line-segment approximation
# Line 2025  namespace LinuxSampler { namespace gig { Line 2098  namespace LinuxSampler { namespace gig {
2098          return y;          return y;
2099      }      }
2100    
     /**  
      * 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;  
     }  
   
2101  }} // namespace LinuxSampler::gig  }} // namespace LinuxSampler::gig

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

  ViewVC Help
Powered by ViewVC