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

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

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

revision 630 by persson, Sat Jun 11 14:51:49 2005 UTC revision 2015 by iliev, Sun Oct 25 22:22:52 2009 UTC
# Line 3  Line 3 
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 Christian Schoenebeck                              *   *   Copyright (C) 2005 - 2009 Christian Schoenebeck                       *
7     *   Copyright (C) 2009 Grigor Iliev                                       *
8   *                                                                         *   *                                                                         *
9   *   This program is free software; you can redistribute it and/or modify  *   *   This program is free software; you can redistribute it and/or modify  *
10   *   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 21  Line 22 
22   *   MA  02111-1307  USA                                                   *   *   MA  02111-1307  USA                                                   *
23   ***************************************************************************/   ***************************************************************************/
24    
 #include "EGADSR.h"  
 #include "Manipulator.h"  
25  #include "../../common/Features.h"  #include "../../common/Features.h"
26  #include "Synthesizer.h"  #include "Synthesizer.h"
27    #include "Profiler.h"
28    #include "Engine.h"
29    #include "EngineChannel.h"
30    
31  #include "Voice.h"  #include "Voice.h"
32    
33  namespace LinuxSampler { namespace gig {  namespace LinuxSampler { namespace gig {
34    
35      const float Voice::FILTER_CUTOFF_COEFF(CalculateFilterCutoffCoeff());      Voice::Voice() {
36            pEngine     = NULL;
37        }
38    
39      const int Voice::FILTER_UPDATE_MASK(CalculateFilterUpdateMask());      Voice::~Voice() {
40            
41        }
42    
43      float Voice::CalculateFilterCutoffCoeff() {      EngineChannel* Voice::GetGigEngineChannel() {
44          return log(CONFIG_FILTER_CUTOFF_MIN / CONFIG_FILTER_CUTOFF_MAX);          return static_cast<EngineChannel*>(pEngineChannel);
45      }      }
46    
47      int Voice::CalculateFilterUpdateMask() {      void Voice::SetEngine(LinuxSampler::Engine* pEngine) {
48          if (CONFIG_FILTER_UPDATE_STEPS <= 0) return 0;          Engine* engine = static_cast<Engine*>(pEngine);
49          int power_of_two;          this->pEngine     = engine;
50          for (power_of_two = 0; 1<<power_of_two < CONFIG_FILTER_UPDATE_STEPS; power_of_two++);          this->pDiskThread = engine->pDiskThread;
51          return (1 << power_of_two) - 1;          dmsg(6,("Voice::SetEngine()\n"));
52      }      }
53    
54      Voice::Voice() {      Voice::SampleInfo Voice::GetSampleInfo() {
55          pEngine     = NULL;          SampleInfo si;
56          pDiskThread = NULL;          si.SampleRate       = pSample->SamplesPerSecond;
57          PlaybackState = playback_state_end;          si.ChannelCount     = pSample->Channels;
58          pEG1   = NULL;          si.FrameSize        = pSample->FrameSize;
59          pEG2   = NULL;          si.BitDepth         = pSample->BitDepth;
60          pEG3   = NULL;          si.TotalFrameCount  = pSample->SamplesTotal;
         pVCAManipulator  = NULL;  
         pVCFCManipulator = NULL;  
         pVCOManipulator  = NULL;  
         pLFO1  = NULL;  
         pLFO2  = NULL;  
         pLFO3  = NULL;  
         KeyGroup = 0;  
         SynthesisMode = 0; // set all mode bits to 0 first  
         // select synthesis implementation (currently either pure C++ or MMX+SSE(1))  
         #if CONFIG_ASM && ARCH_X86  
         SYNTHESIS_MODE_SET_IMPLEMENTATION(SynthesisMode, Features::supportsMMX() && Features::supportsSSE());  
         #else  
         SYNTHESIS_MODE_SET_IMPLEMENTATION(SynthesisMode, false);  
         #endif  
         SYNTHESIS_MODE_SET_PROFILING(SynthesisMode, true);  
61    
62          FilterLeft.Reset();          si.HasLoops       = pRegion->SampleLoops;
63          FilterRight.Reset();          si.LoopStart      = (si.HasLoops) ? pRegion->pSampleLoops[0].LoopStart  : 0;
64            si.LoopLength     = (si.HasLoops) ? pRegion->pSampleLoops[0].LoopLength : 0;
65            si.LoopPlayCount  = pSample->LoopPlayCount;
66            si.Unpitched      = !pRegion->PitchTrack;
67    
68            return si;
69      }      }
70    
71      Voice::~Voice() {      Voice::RegionInfo Voice::GetRegionInfo() {
72          if (pEG1)  delete pEG1;          RegionInfo ri;
73          if (pEG2)  delete pEG2;          ri.UnityNote = pRegion->UnityNote;
74          if (pEG3)  delete pEG3;          ri.FineTune  = pRegion->FineTune;
75          if (pLFO1) delete pLFO1;          ri.Pan       = pRegion->Pan;
76          if (pLFO2) delete pLFO2;          ri.SampleStartOffset = pRegion->SampleStartOffset;
         if (pLFO3) delete pLFO3;  
         if (pVCAManipulator)  delete pVCAManipulator;  
         if (pVCFCManipulator) delete pVCFCManipulator;  
         if (pVCOManipulator)  delete pVCOManipulator;  
     }  
   
     void Voice::SetEngine(Engine* pEngine) {  
         this->pEngine = pEngine;  
   
         // delete old objects  
         if (pEG1) delete pEG1;  
         if (pEG2) delete pEG2;  
         if (pEG3) delete pEG3;  
         if (pVCAManipulator)  delete pVCAManipulator;  
         if (pVCFCManipulator) delete pVCFCManipulator;  
         if (pVCOManipulator)  delete pVCOManipulator;  
         if (pLFO1) delete pLFO1;  
         if (pLFO2) delete pLFO2;  
         if (pLFO3) delete pLFO3;  
   
         // create new ones  
         pEG1   = new EGADSR(pEngine, Event::destination_vca);  
         pEG2   = new EGADSR(pEngine, Event::destination_vcfc);  
         pEG3   = new EGDecay(pEngine, Event::destination_vco);  
         pVCAManipulator  = new VCAManipulator(pEngine);  
         pVCFCManipulator = new VCFCManipulator(pEngine);  
         pVCOManipulator  = new VCOManipulator(pEngine);  
         pLFO1  = new LFO<gig::VCAManipulator>(0.0f, 1.0f, LFO<VCAManipulator>::propagation_top_down, pVCAManipulator, pEngine->pEventPool);  
         pLFO2  = new LFO<gig::VCFCManipulator>(0.0f, 1.0f, LFO<VCFCManipulator>::propagation_top_down, pVCFCManipulator, pEngine->pEventPool);  
         pLFO3  = new LFO<gig::VCOManipulator>(-1200.0f, 1200.0f, LFO<VCOManipulator>::propagation_middle_balanced, pVCOManipulator, pEngine->pEventPool); // +-1 octave (+-1200 cents) max.  
77    
78          this->pDiskThread = pEngine->pDiskThread;          ri.EG1PreAttack        = pRegion->EG1PreAttack;
79          dmsg(6,("Voice::SetEngine()\n"));          ri.EG1Attack           = pRegion->EG1Attack;
80      }          ri.EG1Hold             = pRegion->EG1Hold;
81            ri.EG1Decay1           = pRegion->EG1Decay1;
82            ri.EG1Decay2           = pRegion->EG1Decay2;
83            ri.EG1Sustain          = pRegion->EG1Sustain;
84            ri.EG1InfiniteSustain  = pRegion->EG1InfiniteSustain;
85            ri.EG1Release          = pRegion->EG1Release;
86    
87      /**          ri.EG2PreAttack        = pRegion->EG2PreAttack;
88       *  Initializes and triggers the voice, a disk stream will be launched if          ri.EG2Attack           = pRegion->EG2Attack;
89       *  needed.          ri.EG2Decay1           = pRegion->EG2Decay1;
90       *          ri.EG2Decay2           = pRegion->EG2Decay2;
91       *  @param pEngineChannel       - engine channel on which this voice was ordered          ri.EG2Sustain          = pRegion->EG2Sustain;
92       *  @param itNoteOnEvent        - event that caused triggering of this voice          ri.EG2InfiniteSustain  = pRegion->EG2InfiniteSustain;
93       *  @param PitchBend            - MIDI detune factor (-8192 ... +8191)          ri.EG2Release          = pRegion->EG2Release;
      *  @param pInstrument          - points to the loaded instrument which provides sample wave(s) and articulation data  
      *  @param iLayer               - layer number this voice refers to (only if this is a layered sound of course)  
      *  @param ReleaseTriggerVoice  - if this new voice is a release trigger voice (optional, default = false)  
      *  @param VoiceStealingAllowed - wether the voice is allowed to steal voices for further subvoices  
      *  @returns 0 on success, a value < 0 if the voice wasn't triggered  
      *           (either due to an error or e.g. because no region is  
      *           defined for the given key)  
      */  
     int Voice::Trigger(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNoteOnEvent, int PitchBend, ::gig::Instrument* pInstrument, int iLayer, bool ReleaseTriggerVoice, bool VoiceStealingAllowed) {  
         this->pEngineChannel = pEngineChannel;  
         if (!pInstrument) {  
            dmsg(1,("voice::trigger: !pInstrument\n"));  
            exit(EXIT_FAILURE);  
         }  
         #if CONFIG_DEVMODE  
         if (itNoteOnEvent->FragmentPos() > pEngine->MaxSamplesPerCycle) { // just a sanity check for debugging  
             dmsg(1,("Voice::Trigger(): ERROR, TriggerDelay > Totalsamples\n"));  
         }  
         #endif // CONFIG_DEVMODE  
94    
95          Type            = type_normal;          ri.EG3Attack     = pRegion->EG3Attack;
96          MIDIKey         = itNoteOnEvent->Param.Note.Key;          ri.EG3Depth      = pRegion->EG3Depth;
97          pRegion         = pInstrument->GetRegion(MIDIKey);          ri.VCFEnabled    = pRegion->VCFEnabled;
98          PlaybackState   = playback_state_init; // mark voice as triggered, but no audio rendered yet          ri.VCFType       = pRegion->VCFType;
99          Delay           = itNoteOnEvent->FragmentPos();          ri.VCFResonance  = pRegion->VCFResonance;
         itTriggerEvent  = itNoteOnEvent;  
         itKillEvent     = Pool<Event>::Iterator();  
   
         if (!pRegion) {  
             dmsg(4, ("gig::Voice: No Region defined for MIDI key %d\n", MIDIKey));  
             return -1;  
         }  
100    
101          // only mark the first voice of a layered voice (group) to be in a          ri.ReleaseTriggerDecay = pRegion->ReleaseTriggerDecay;
         // key group, so the layered voices won't kill each other  
         KeyGroup = (iLayer == 0 && !ReleaseTriggerVoice) ? pRegion->KeyGroup : 0;  
   
         // get current dimension values to select the right dimension region  
         //FIXME: controller values for selecting the dimension region here are currently not sample accurate  
         uint DimValues[8] = { 0 };  
         for (int i = pRegion->Dimensions - 1; i >= 0; i--) {  
             switch (pRegion->pDimensionDefinitions[i].dimension) {  
                 case ::gig::dimension_samplechannel:  
                     DimValues[i] = 0; //TODO: we currently ignore this dimension  
                     break;  
                 case ::gig::dimension_layer:  
                     DimValues[i] = iLayer;  
                     break;  
                 case ::gig::dimension_velocity:  
                     DimValues[i] = itNoteOnEvent->Param.Note.Velocity;  
                     break;  
                 case ::gig::dimension_channelaftertouch:  
                     DimValues[i] = 0; //TODO: we currently ignore this dimension  
                     break;  
                 case ::gig::dimension_releasetrigger:  
                     Type = (ReleaseTriggerVoice) ? type_release_trigger : (!iLayer) ? type_release_trigger_required : type_normal;  
                     DimValues[i] = (uint) ReleaseTriggerVoice;  
                     break;  
                 case ::gig::dimension_keyboard:  
                     DimValues[i] = (uint) pEngineChannel->CurrentKeyDimension;  
                     break;  
                 case ::gig::dimension_roundrobin:  
                     DimValues[i] = (uint) pEngineChannel->pMIDIKeyInfo[MIDIKey].RoundRobinIndex; // incremented for each note on  
                     break;  
                 case ::gig::dimension_random:  
                     pEngine->RandomSeed = pEngine->RandomSeed * 1103515245 + 12345; // classic pseudo random number generator  
                     DimValues[i] = (uint) pEngine->RandomSeed >> (32 - pRegion->pDimensionDefinitions[i].bits); // highest bits are most random  
                     break;  
                 case ::gig::dimension_modwheel:  
                     DimValues[i] = pEngineChannel->ControllerTable[1];  
                     break;  
                 case ::gig::dimension_breath:  
                     DimValues[i] = pEngineChannel->ControllerTable[2];  
                     break;  
                 case ::gig::dimension_foot:  
                     DimValues[i] = pEngineChannel->ControllerTable[4];  
                     break;  
                 case ::gig::dimension_portamentotime:  
                     DimValues[i] = pEngineChannel->ControllerTable[5];  
                     break;  
                 case ::gig::dimension_effect1:  
                     DimValues[i] = pEngineChannel->ControllerTable[12];  
                     break;  
                 case ::gig::dimension_effect2:  
                     DimValues[i] = pEngineChannel->ControllerTable[13];  
                     break;  
                 case ::gig::dimension_genpurpose1:  
                     DimValues[i] = pEngineChannel->ControllerTable[16];  
                     break;  
                 case ::gig::dimension_genpurpose2:  
                     DimValues[i] = pEngineChannel->ControllerTable[17];  
                     break;  
                 case ::gig::dimension_genpurpose3:  
                     DimValues[i] = pEngineChannel->ControllerTable[18];  
                     break;  
                 case ::gig::dimension_genpurpose4:  
                     DimValues[i] = pEngineChannel->ControllerTable[19];  
                     break;  
                 case ::gig::dimension_sustainpedal:  
                     DimValues[i] = pEngineChannel->ControllerTable[64];  
                     break;  
                 case ::gig::dimension_portamento:  
                     DimValues[i] = pEngineChannel->ControllerTable[65];  
                     break;  
                 case ::gig::dimension_sostenutopedal:  
                     DimValues[i] = pEngineChannel->ControllerTable[66];  
                     break;  
                 case ::gig::dimension_softpedal:  
                     DimValues[i] = pEngineChannel->ControllerTable[67];  
                     break;  
                 case ::gig::dimension_genpurpose5:  
                     DimValues[i] = pEngineChannel->ControllerTable[80];  
                     break;  
                 case ::gig::dimension_genpurpose6:  
                     DimValues[i] = pEngineChannel->ControllerTable[81];  
                     break;  
                 case ::gig::dimension_genpurpose7:  
                     DimValues[i] = pEngineChannel->ControllerTable[82];  
                     break;  
                 case ::gig::dimension_genpurpose8:  
                     DimValues[i] = pEngineChannel->ControllerTable[83];  
                     break;  
                 case ::gig::dimension_effect1depth:  
                     DimValues[i] = pEngineChannel->ControllerTable[91];  
                     break;  
                 case ::gig::dimension_effect2depth:  
                     DimValues[i] = pEngineChannel->ControllerTable[92];  
                     break;  
                 case ::gig::dimension_effect3depth:  
                     DimValues[i] = pEngineChannel->ControllerTable[93];  
                     break;  
                 case ::gig::dimension_effect4depth:  
                     DimValues[i] = pEngineChannel->ControllerTable[94];  
                     break;  
                 case ::gig::dimension_effect5depth:  
                     DimValues[i] = pEngineChannel->ControllerTable[95];  
                     break;  
                 case ::gig::dimension_none:  
                     std::cerr << "gig::Voice::Trigger() Error: dimension=none\n" << std::flush;  
                     break;  
                 default:  
                     std::cerr << "gig::Voice::Trigger() Error: Unknown dimension\n" << std::flush;  
             }  
         }  
         pDimRgn = pRegion->GetDimensionRegionByValue(DimValues);  
102    
103          pSample = pDimRgn->pSample; // sample won't change until the voice is finished          return ri;
104          if (!pSample || !pSample->SamplesTotal) return -1; // no need to continue if sample is silent      }
105    
106          // calculate volume      Voice::InstrumentInfo Voice::GetInstrumentInfo() {
107          const double velocityAttenuation = pDimRgn->GetVelocityAttenuation(itNoteOnEvent->Param.Note.Velocity);          InstrumentInfo ii;
108            ii.FineTune = GetGigEngineChannel()->pInstrument->FineTune;
109            ii.PitchbendRange = GetGigEngineChannel()->pInstrument->PitchbendRange;
110    
111          Volume = velocityAttenuation / 32768.0f; // we downscale by 32768 to convert from int16 value range to DSP value range (which is -1.0..1.0)          return ii;
112        }
113    
114          Volume *= pDimRgn->SampleAttenuation;      double Voice::GetSampleAttenuation() {
115            return pRegion->SampleAttenuation;
116        }
117    
118          // the volume of release triggered samples depends on note length      double Voice::GetVelocityAttenuation(uint8_t MIDIKeyVelocity) {
119          if (ReleaseTriggerVoice) {          return pRegion->GetVelocityAttenuation(MIDIKeyVelocity);
120              float noteLength = float(pEngine->FrameTime + Delay -      }
121                                       pEngineChannel->pMIDIKeyInfo[MIDIKey].NoteOnTime) / pEngine->SampleRate;  
122              float attenuation = 1 - 0.01053 * (256 >> pDimRgn->ReleaseTriggerDecay) * noteLength;      double Voice::GetVelocityRelease(uint8_t MIDIKeyVelocity) {
123              if (attenuation <= 0) return -1;          return pRegion->GetVelocityRelease(MIDIKeyVelocity);
124              Volume *= attenuation;      }
125    
126        void Voice::ProcessCCEvent(RTList<Event>::Iterator& itEvent) {
127            if (itEvent->Type == Event::type_control_change && itEvent->Param.CC.Controller) { // if (valid) MIDI control change event
128                if (pRegion->AttenuationController.type == ::gig::attenuation_ctrl_t::type_controlchange &&
129                    itEvent->Param.CC.Controller == pRegion->AttenuationController.controller_number) {
130                    CrossfadeSmoother.update(AbstractEngine::CrossfadeCurve[CrossfadeAttenuation(itEvent->Param.CC.Value)]);
131                }
132          }          }
133        }
134    
135        void Voice::ProcessCutoffEvent(RTList<Event>::Iterator& itEvent) {
136            int ccvalue = itEvent->Param.CC.Value;
137            if (VCFCutoffCtrl.value == ccvalue) return;
138            VCFCutoffCtrl.value == ccvalue;
139            if (pRegion->VCFCutoffControllerInvert)  ccvalue = 127 - ccvalue;
140            if (ccvalue < pRegion->VCFVelocityScale) ccvalue = pRegion->VCFVelocityScale;
141            float cutoff = CutoffBase * float(ccvalue);
142            if (cutoff > 127.0f) cutoff = 127.0f;
143    
144          // select channel mode (mono or stereo)          VCFCutoffCtrl.fvalue = cutoff; // needed for initialization of fFinalCutoff next time
145          SYNTHESIS_MODE_SET_CHANNELS(SynthesisMode, pSample->Channels == 2);          fFinalCutoff = cutoff;
146        }
147    
148          // get starting crossfade volume level      double Voice::CalculateCrossfadeVolume(uint8_t MIDIKeyVelocity) {
149          switch (pDimRgn->AttenuationController.type) {          float crossfadeVolume;
150            switch (pRegion->AttenuationController.type) {
151              case ::gig::attenuation_ctrl_t::type_channelaftertouch:              case ::gig::attenuation_ctrl_t::type_channelaftertouch:
152                  CrossfadeVolume = 1.0f; //TODO: aftertouch not supported yet                  crossfadeVolume = Engine::CrossfadeCurve[CrossfadeAttenuation(GetGigEngineChannel()->ControllerTable[128])];
153                  break;                  break;
154              case ::gig::attenuation_ctrl_t::type_velocity:              case ::gig::attenuation_ctrl_t::type_velocity:
155                  CrossfadeVolume = CrossfadeAttenuation(itNoteOnEvent->Param.Note.Velocity);                  crossfadeVolume = Engine::CrossfadeCurve[CrossfadeAttenuation(MIDIKeyVelocity)];
156                  break;                  break;
157              case ::gig::attenuation_ctrl_t::type_controlchange: //FIXME: currently not sample accurate              case ::gig::attenuation_ctrl_t::type_controlchange: //FIXME: currently not sample accurate
158                  CrossfadeVolume = CrossfadeAttenuation(pEngineChannel->ControllerTable[pDimRgn->AttenuationController.controller_number]);                  crossfadeVolume = Engine::CrossfadeCurve[CrossfadeAttenuation(GetGigEngineChannel()->ControllerTable[pRegion->AttenuationController.controller_number])];
159                  break;                  break;
160              case ::gig::attenuation_ctrl_t::type_none: // no crossfade defined              case ::gig::attenuation_ctrl_t::type_none: // no crossfade defined
161              default:              default:
162                  CrossfadeVolume = 1.0f;                  crossfadeVolume = 1.0f;
163          }          }
164    
165          PanLeft  = 1.0f - float(RTMath::Max(pDimRgn->Pan, 0)) /  63.0f;          return crossfadeVolume;
166          PanRight = 1.0f - float(RTMath::Min(pDimRgn->Pan, 0)) / -64.0f;      }
   
         Pos = pDimRgn->SampleStartOffset; // offset where we should start playback of sample (0 - 2000 sample points)  
   
         // Check if the sample needs disk streaming or is too short for that  
         long cachedsamples = pSample->GetCache().Size / pSample->FrameSize;  
         DiskVoice          = cachedsamples < pSample->SamplesTotal;  
   
         if (DiskVoice) { // voice to be streamed from disk  
             MaxRAMPos = cachedsamples - (pEngine->MaxSamplesPerCycle << CONFIG_MAX_PITCH) / pSample->Channels; //TODO: this calculation is too pessimistic and may better be moved to Render() method, so it calculates MaxRAMPos dependent to the current demand of sample points to be rendered (e.g. in case of JACK)  
   
             // check if there's a loop defined which completely fits into the cached (RAM) part of the sample  
             if (pSample->Loops && pSample->LoopEnd <= MaxRAMPos) {  
                 RAMLoop        = true;  
                 LoopCyclesLeft = pSample->LoopPlayCount;  
             }  
             else RAMLoop = false;  
   
             if (pDiskThread->OrderNewStream(&DiskStreamRef, pSample, MaxRAMPos, !RAMLoop) < 0) {  
                 dmsg(1,("Disk stream order failed!\n"));  
                 KillImmediately();  
                 return -1;  
             }  
             dmsg(4,("Disk voice launched (cached samples: %d, total Samples: %d, MaxRAMPos: %d, RAMLooping: %s)\n", cachedsamples, pSample->SamplesTotal, MaxRAMPos, (RAMLoop) ? "yes" : "no"));  
         }  
         else { // RAM only voice  
             MaxRAMPos = cachedsamples;  
             if (pSample->Loops) {  
                 RAMLoop        = true;  
                 LoopCyclesLeft = pSample->LoopPlayCount;  
             }  
             else RAMLoop = false;  
             dmsg(4,("RAM only voice launched (Looping: %s)\n", (RAMLoop) ? "yes" : "no"));  
         }  
   
   
         // calculate initial pitch value  
         {  
             double pitchbasecents = pDimRgn->FineTune + (int) pEngine->ScaleTuning[MIDIKey % 12];  
             if (pDimRgn->PitchTrack) pitchbasecents += (MIDIKey - (int) pDimRgn->UnityNote) * 100;  
             this->PitchBase = RTMath::CentsToFreqRatio(pitchbasecents) * (double(pSample->SamplesPerSecond) / double(pEngine->pAudioOutputDevice->SampleRate()));  
             this->PitchBend = RTMath::CentsToFreqRatio(((double) PitchBend / 8192.0) * 200.0); // pitchbend wheel +-2 semitones = 200 cents  
         }  
   
         // the length of the decay and release curves are dependent on the velocity  
         const double velrelease = 1 / pDimRgn->GetVelocityRelease(itNoteOnEvent->Param.Note.Velocity);  
   
         // setup EG 1 (VCA EG)  
         {  
             // get current value of EG1 controller  
             double eg1controllervalue;  
             switch (pDimRgn->EG1Controller.type) {  
                 case ::gig::eg1_ctrl_t::type_none: // no controller defined  
                     eg1controllervalue = 0;  
                     break;  
                 case ::gig::eg1_ctrl_t::type_channelaftertouch:  
                     eg1controllervalue = 0; // TODO: aftertouch not yet supported  
                     break;  
                 case ::gig::eg1_ctrl_t::type_velocity:  
                     eg1controllervalue = itNoteOnEvent->Param.Note.Velocity;  
                     break;  
                 case ::gig::eg1_ctrl_t::type_controlchange: // MIDI control change controller  
                     eg1controllervalue = pEngineChannel->ControllerTable[pDimRgn->EG1Controller.controller_number];  
                     break;  
             }  
             if (pDimRgn->EG1ControllerInvert) eg1controllervalue = 127 - eg1controllervalue;  
167    
168              // calculate influence of EG1 controller on EG1's parameters (TODO: needs to be fine tuned)      double Voice::GetEG1ControllerValue(uint8_t MIDIKeyVelocity) {
169              double eg1attack  = (pDimRgn->EG1ControllerAttackInfluence)  ? 0.0001 * (double) (1 << pDimRgn->EG1ControllerAttackInfluence)  * eg1controllervalue : 0.0;          double eg1controllervalue = 0;
170              double eg1decay   = (pDimRgn->EG1ControllerDecayInfluence)   ? 0.0001 * (double) (1 << pDimRgn->EG1ControllerDecayInfluence)   * eg1controllervalue : 0.0;          switch (pRegion->EG1Controller.type) {
171              double eg1release = (pDimRgn->EG1ControllerReleaseInfluence) ? 0.0001 * (double) (1 << pDimRgn->EG1ControllerReleaseInfluence) * eg1controllervalue : 0.0;              case ::gig::eg1_ctrl_t::type_none: // no controller defined
172                    eg1controllervalue = 0;
173              pEG1->Trigger(pDimRgn->EG1PreAttack,                  break;
174                            pDimRgn->EG1Attack + eg1attack,              case ::gig::eg1_ctrl_t::type_channelaftertouch:
175                            pDimRgn->EG1Hold,                  eg1controllervalue = GetGigEngineChannel()->ControllerTable[128];
176                            pSample->LoopStart,                  break;
177                            (pDimRgn->EG1Decay1 + eg1decay) * velrelease,              case ::gig::eg1_ctrl_t::type_velocity:
178                            (pDimRgn->EG1Decay2 + eg1decay) * velrelease,                  eg1controllervalue = MIDIKeyVelocity;
179                            pDimRgn->EG1InfiniteSustain,                  break;
180                            pDimRgn->EG1Sustain,              case ::gig::eg1_ctrl_t::type_controlchange: // MIDI control change controller
181                            (pDimRgn->EG1Release + eg1release) * velrelease,                  eg1controllervalue = GetGigEngineChannel()->ControllerTable[pRegion->EG1Controller.controller_number];
182                            // the SSE synthesis implementation requires                  break;
                           // the vca start to be 16 byte aligned  
                           SYNTHESIS_MODE_GET_IMPLEMENTATION(SynthesisMode) ?  
                           Delay & 0xfffffffc : Delay,  
                           velocityAttenuation);  
183          }          }
184            if (pRegion->EG1ControllerInvert) eg1controllervalue = 127 - eg1controllervalue;
185    
186            return eg1controllervalue;
187        }
188    
189          // setup EG 2 (VCF Cutoff EG)      Voice::EGInfo Voice::CalculateEG1ControllerInfluence(double eg1ControllerValue) {
190          {          EGInfo eg;
191              // get current value of EG2 controller          // (eg1attack is different from the others)
192              double eg2controllervalue;          eg.Attack  = (pRegion->EG1ControllerAttackInfluence)  ?
193              switch (pDimRgn->EG2Controller.type) {              1 + 0.031 * (double) (pRegion->EG1ControllerAttackInfluence == 1 ?
194                  case ::gig::eg2_ctrl_t::type_none: // no controller defined                                    1 : 1 << pRegion->EG1ControllerAttackInfluence) * eg1ControllerValue : 1.0;
195                      eg2controllervalue = 0;          eg.Decay   = (pRegion->EG1ControllerDecayInfluence)   ? 1 + 0.00775 * (double) (1 << pRegion->EG1ControllerDecayInfluence)   * eg1ControllerValue : 1.0;
196                      break;          eg.Release = (pRegion->EG1ControllerReleaseInfluence) ? 1 + 0.00775 * (double) (1 << pRegion->EG1ControllerReleaseInfluence) * eg1ControllerValue : 1.0;
                 case ::gig::eg2_ctrl_t::type_channelaftertouch:  
                     eg2controllervalue = 0; // TODO: aftertouch not yet supported  
                     break;  
                 case ::gig::eg2_ctrl_t::type_velocity:  
                     eg2controllervalue = itNoteOnEvent->Param.Note.Velocity;  
                     break;  
                 case ::gig::eg2_ctrl_t::type_controlchange: // MIDI control change controller  
                     eg2controllervalue = pEngineChannel->ControllerTable[pDimRgn->EG2Controller.controller_number];  
                     break;  
             }  
             if (pDimRgn->EG2ControllerInvert) eg2controllervalue = 127 - eg2controllervalue;  
   
             // calculate influence of EG2 controller on EG2's parameters (TODO: needs to be fine tuned)  
             double eg2attack  = (pDimRgn->EG2ControllerAttackInfluence)  ? 0.0001 * (double) (1 << pDimRgn->EG2ControllerAttackInfluence)  * eg2controllervalue : 0.0;  
             double eg2decay   = (pDimRgn->EG2ControllerDecayInfluence)   ? 0.0001 * (double) (1 << pDimRgn->EG2ControllerDecayInfluence)   * eg2controllervalue : 0.0;  
             double eg2release = (pDimRgn->EG2ControllerReleaseInfluence) ? 0.0001 * (double) (1 << pDimRgn->EG2ControllerReleaseInfluence) * eg2controllervalue : 0.0;  
   
             pEG2->Trigger(pDimRgn->EG2PreAttack,  
                           pDimRgn->EG2Attack + eg2attack,  
                           false,  
                           pSample->LoopStart,  
                           (pDimRgn->EG2Decay1 + eg2decay) * velrelease,  
                           (pDimRgn->EG2Decay2 + eg2decay) * velrelease,  
                           pDimRgn->EG2InfiniteSustain,  
                           pDimRgn->EG2Sustain,  
                           (pDimRgn->EG2Release + eg2release) * velrelease,  
                           Delay,  
                           velocityAttenuation);  
         }  
197    
198            return eg;
199        }
200    
201          // setup EG 3 (VCO EG)      double Voice::GetEG2ControllerValue(uint8_t MIDIKeyVelocity) {
202          {          double eg2controllervalue = 0;
203            double eg3depth = RTMath::CentsToFreqRatio(pDimRgn->EG3Depth);          switch (pRegion->EG2Controller.type) {
204            pEG3->Trigger(eg3depth, pDimRgn->EG3Attack, Delay);              case ::gig::eg2_ctrl_t::type_none: // no controller defined
205                    eg2controllervalue = 0;
206                    break;
207                case ::gig::eg2_ctrl_t::type_channelaftertouch:
208                    eg2controllervalue = GetGigEngineChannel()->ControllerTable[128];
209                    break;
210                case ::gig::eg2_ctrl_t::type_velocity:
211                    eg2controllervalue = MIDIKeyVelocity;
212                    break;
213                case ::gig::eg2_ctrl_t::type_controlchange: // MIDI control change controller
214                    eg2controllervalue = GetGigEngineChannel()->ControllerTable[pRegion->EG2Controller.controller_number];
215                    break;
216          }          }
217            if (pRegion->EG2ControllerInvert) eg2controllervalue = 127 - eg2controllervalue;
218    
219            return eg2controllervalue;
220        }
221    
222          // setup LFO 1 (VCA LFO)      Voice::EGInfo Voice::CalculateEG2ControllerInfluence(double eg2ControllerValue) {
223          {          EGInfo eg;
224              uint16_t lfo1_internal_depth;          eg.Attack  = (pRegion->EG2ControllerAttackInfluence)  ? 1 + 0.00775 * (double) (1 << pRegion->EG2ControllerAttackInfluence)  * eg2ControllerValue : 1.0;
225              switch (pDimRgn->LFO1Controller) {          eg.Decay   = (pRegion->EG2ControllerDecayInfluence)   ? 1 + 0.00775 * (double) (1 << pRegion->EG2ControllerDecayInfluence)   * eg2ControllerValue : 1.0;
226                  case ::gig::lfo1_ctrl_internal:          eg.Release = (pRegion->EG2ControllerReleaseInfluence) ? 1 + 0.00775 * (double) (1 << pRegion->EG2ControllerReleaseInfluence) * eg2ControllerValue : 1.0;
                     lfo1_internal_depth  = pDimRgn->LFO1InternalDepth;  
                     pLFO1->ExtController = 0; // no external controller  
                     break;  
                 case ::gig::lfo1_ctrl_modwheel:  
                     lfo1_internal_depth  = 0;  
                     pLFO1->ExtController = 1; // MIDI controller 1  
                     break;  
                 case ::gig::lfo1_ctrl_breath:  
                     lfo1_internal_depth  = 0;  
                     pLFO1->ExtController = 2; // MIDI controller 2  
                     break;  
                 case ::gig::lfo1_ctrl_internal_modwheel:  
                     lfo1_internal_depth  = pDimRgn->LFO1InternalDepth;  
                     pLFO1->ExtController = 1; // MIDI controller 1  
                     break;  
                 case ::gig::lfo1_ctrl_internal_breath:  
                     lfo1_internal_depth  = pDimRgn->LFO1InternalDepth;  
                     pLFO1->ExtController = 2; // MIDI controller 2  
                     break;  
                 default:  
                     lfo1_internal_depth  = 0;  
                     pLFO1->ExtController = 0; // no external controller  
             }  
             pLFO1->Trigger(pDimRgn->LFO1Frequency,  
                           lfo1_internal_depth,  
                           pDimRgn->LFO1ControlDepth,  
                           pEngineChannel->ControllerTable[pLFO1->ExtController],  
                           pDimRgn->LFO1FlipPhase,  
                           pEngine->SampleRate,  
                           Delay);  
         }  
227    
228            return eg;
229        }
230    
231          // setup LFO 2 (VCF Cutoff LFO)      void Voice::InitLFO1() {
232          {          uint16_t lfo1_internal_depth;
233              uint16_t lfo2_internal_depth;          switch (pRegion->LFO1Controller) {
234              switch (pDimRgn->LFO2Controller) {              case ::gig::lfo1_ctrl_internal:
235                  case ::gig::lfo2_ctrl_internal:                  lfo1_internal_depth  = pRegion->LFO1InternalDepth;
236                      lfo2_internal_depth  = pDimRgn->LFO2InternalDepth;                  pLFO1->ExtController = 0; // no external controller
237                      pLFO2->ExtController = 0; // no external controller                  bLFO1Enabled         = (lfo1_internal_depth > 0);
238                      break;                  break;
239                  case ::gig::lfo2_ctrl_modwheel:              case ::gig::lfo1_ctrl_modwheel:
240                      lfo2_internal_depth  = 0;                  lfo1_internal_depth  = 0;
241                      pLFO2->ExtController = 1; // MIDI controller 1                  pLFO1->ExtController = 1; // MIDI controller 1
242                      break;                  bLFO1Enabled         = (pRegion->LFO1ControlDepth > 0);
243                  case ::gig::lfo2_ctrl_foot:                  break;
244                      lfo2_internal_depth  = 0;              case ::gig::lfo1_ctrl_breath:
245                      pLFO2->ExtController = 4; // MIDI controller 4                  lfo1_internal_depth  = 0;
246                      break;                  pLFO1->ExtController = 2; // MIDI controller 2
247                  case ::gig::lfo2_ctrl_internal_modwheel:                  bLFO1Enabled         = (pRegion->LFO1ControlDepth > 0);
248                      lfo2_internal_depth  = pDimRgn->LFO2InternalDepth;                  break;
249                      pLFO2->ExtController = 1; // MIDI controller 1              case ::gig::lfo1_ctrl_internal_modwheel:
250                      break;                  lfo1_internal_depth  = pRegion->LFO1InternalDepth;
251                  case ::gig::lfo2_ctrl_internal_foot:                  pLFO1->ExtController = 1; // MIDI controller 1
252                      lfo2_internal_depth  = pDimRgn->LFO2InternalDepth;                  bLFO1Enabled         = (lfo1_internal_depth > 0 || pRegion->LFO1ControlDepth > 0);
253                      pLFO2->ExtController = 4; // MIDI controller 4                  break;
254                      break;              case ::gig::lfo1_ctrl_internal_breath:
255                  default:                  lfo1_internal_depth  = pRegion->LFO1InternalDepth;
256                      lfo2_internal_depth  = 0;                  pLFO1->ExtController = 2; // MIDI controller 2
257                      pLFO2->ExtController = 0; // no external controller                  bLFO1Enabled         = (lfo1_internal_depth > 0 || pRegion->LFO1ControlDepth > 0);
258              }                  break;
259              pLFO2->Trigger(pDimRgn->LFO2Frequency,              default:
260                            lfo2_internal_depth,                  lfo1_internal_depth  = 0;
261                            pDimRgn->LFO2ControlDepth,                  pLFO1->ExtController = 0; // no external controller
262                            pEngineChannel->ControllerTable[pLFO2->ExtController],                  bLFO1Enabled         = false;
263                            pDimRgn->LFO2FlipPhase,          }
264                            pEngine->SampleRate,          if (bLFO1Enabled) {
265                            Delay);              pLFO1->trigger(pRegion->LFO1Frequency,
266                               start_level_min,
267                               lfo1_internal_depth,
268                               pRegion->LFO1ControlDepth,
269                               pRegion->LFO1FlipPhase,
270                               pEngine->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE);
271                pLFO1->update(pLFO1->ExtController ? GetGigEngineChannel()->ControllerTable[pLFO1->ExtController] : 0);
272          }          }
273        }
274    
275        void Voice::InitLFO2() {
276          // setup LFO 3 (VCO LFO)          uint16_t lfo2_internal_depth;
277          {          switch (pRegion->LFO2Controller) {
278              uint16_t lfo3_internal_depth;              case ::gig::lfo2_ctrl_internal:
279              switch (pDimRgn->LFO3Controller) {                  lfo2_internal_depth  = pRegion->LFO2InternalDepth;
280                  case ::gig::lfo3_ctrl_internal:                  pLFO2->ExtController = 0; // no external controller
281                      lfo3_internal_depth  = pDimRgn->LFO3InternalDepth;                  bLFO2Enabled         = (lfo2_internal_depth > 0);
282                      pLFO3->ExtController = 0; // no external controller                  break;
283                      break;              case ::gig::lfo2_ctrl_modwheel:
284                  case ::gig::lfo3_ctrl_modwheel:                  lfo2_internal_depth  = 0;
285                      lfo3_internal_depth  = 0;                  pLFO2->ExtController = 1; // MIDI controller 1
286                      pLFO3->ExtController = 1; // MIDI controller 1                  bLFO2Enabled         = (pRegion->LFO2ControlDepth > 0);
287                      break;                  break;
288                  case ::gig::lfo3_ctrl_aftertouch:              case ::gig::lfo2_ctrl_foot:
289                      lfo3_internal_depth  = 0;                  lfo2_internal_depth  = 0;
290                      pLFO3->ExtController = 0; // TODO: aftertouch not implemented yet                  pLFO2->ExtController = 4; // MIDI controller 4
291                      break;                  bLFO2Enabled         = (pRegion->LFO2ControlDepth > 0);
292                  case ::gig::lfo3_ctrl_internal_modwheel:                  break;
293                      lfo3_internal_depth  = pDimRgn->LFO3InternalDepth;              case ::gig::lfo2_ctrl_internal_modwheel:
294                      pLFO3->ExtController = 1; // MIDI controller 1                  lfo2_internal_depth  = pRegion->LFO2InternalDepth;
295                      break;                  pLFO2->ExtController = 1; // MIDI controller 1
296                  case ::gig::lfo3_ctrl_internal_aftertouch:                  bLFO2Enabled         = (lfo2_internal_depth > 0 || pRegion->LFO2ControlDepth > 0);
297                      lfo3_internal_depth  = pDimRgn->LFO3InternalDepth;                  break;
298                      pLFO1->ExtController = 0; // TODO: aftertouch not implemented yet              case ::gig::lfo2_ctrl_internal_foot:
299                      break;                  lfo2_internal_depth  = pRegion->LFO2InternalDepth;
300                  default:                  pLFO2->ExtController = 4; // MIDI controller 4
301                      lfo3_internal_depth  = 0;                  bLFO2Enabled         = (lfo2_internal_depth > 0 || pRegion->LFO2ControlDepth > 0);
302                      pLFO3->ExtController = 0; // no external controller                  break;
303              }              default:
304              pLFO3->Trigger(pDimRgn->LFO3Frequency,                  lfo2_internal_depth  = 0;
305                            lfo3_internal_depth,                  pLFO2->ExtController = 0; // no external controller
306                            pDimRgn->LFO3ControlDepth,                  bLFO2Enabled         = false;
307                            pEngineChannel->ControllerTable[pLFO3->ExtController],          }
308                            false,          if (bLFO2Enabled) {
309                            pEngine->SampleRate,              pLFO2->trigger(pRegion->LFO2Frequency,
310                            Delay);                             start_level_max,
311                               lfo2_internal_depth,
312                               pRegion->LFO2ControlDepth,
313                               pRegion->LFO2FlipPhase,
314                               pEngine->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE);
315                pLFO2->update(pLFO2->ExtController ? GetGigEngineChannel()->ControllerTable[pLFO2->ExtController] : 0);
316          }          }
317        }
318    
319        void Voice::InitLFO3() {
320          #if CONFIG_FORCE_FILTER          uint16_t lfo3_internal_depth;
321          const bool bUseFilter = true;          switch (pRegion->LFO3Controller) {
322          #else // use filter only if instrument file told so              case ::gig::lfo3_ctrl_internal:
323          const bool bUseFilter = pDimRgn->VCFEnabled;                  lfo3_internal_depth  = pRegion->LFO3InternalDepth;
324          #endif // CONFIG_FORCE_FILTER                  pLFO3->ExtController = 0; // no external controller
325          SYNTHESIS_MODE_SET_FILTER(SynthesisMode, bUseFilter);                  bLFO3Enabled         = (lfo3_internal_depth > 0);
326          if (bUseFilter) {                  break;
327              #ifdef CONFIG_OVERRIDE_CUTOFF_CTRL              case ::gig::lfo3_ctrl_modwheel:
328              VCFCutoffCtrl.controller = CONFIG_OVERRIDE_CUTOFF_CTRL;                  lfo3_internal_depth  = 0;
329              #else // use the one defined in the instrument file                  pLFO3->ExtController = 1; // MIDI controller 1
330              switch (pDimRgn->VCFCutoffController) {                  bLFO3Enabled         = (pRegion->LFO3ControlDepth > 0);
331                  case ::gig::vcf_cutoff_ctrl_modwheel:                  break;
332                      VCFCutoffCtrl.controller = 1;              case ::gig::lfo3_ctrl_aftertouch:
333                      break;                  lfo3_internal_depth  = 0;
334                  case ::gig::vcf_cutoff_ctrl_effect1:                  pLFO3->ExtController = 128;
335                      VCFCutoffCtrl.controller = 12;                  bLFO3Enabled         = true;
336                      break;                  break;
337                  case ::gig::vcf_cutoff_ctrl_effect2:              case ::gig::lfo3_ctrl_internal_modwheel:
338                      VCFCutoffCtrl.controller = 13;                  lfo3_internal_depth  = pRegion->LFO3InternalDepth;
339                      break;                  pLFO3->ExtController = 1; // MIDI controller 1
340                  case ::gig::vcf_cutoff_ctrl_breath:                  bLFO3Enabled         = (lfo3_internal_depth > 0 || pRegion->LFO3ControlDepth > 0);
341                      VCFCutoffCtrl.controller = 2;                  break;
342                      break;              case ::gig::lfo3_ctrl_internal_aftertouch:
343                  case ::gig::vcf_cutoff_ctrl_foot:                  lfo3_internal_depth  = pRegion->LFO3InternalDepth;
344                      VCFCutoffCtrl.controller = 4;                  pLFO1->ExtController = 128;
345                      break;                  bLFO3Enabled         = (lfo3_internal_depth > 0 || pRegion->LFO3ControlDepth > 0);
346                  case ::gig::vcf_cutoff_ctrl_sustainpedal:                  break;
347                      VCFCutoffCtrl.controller = 64;              default:
348                      break;                  lfo3_internal_depth  = 0;
349                  case ::gig::vcf_cutoff_ctrl_softpedal:                  pLFO3->ExtController = 0; // no external controller
350                      VCFCutoffCtrl.controller = 67;                  bLFO3Enabled         = false;
351                      break;          }
352                  case ::gig::vcf_cutoff_ctrl_genpurpose7:          if (bLFO3Enabled) {
353                      VCFCutoffCtrl.controller = 82;              pLFO3->trigger(pRegion->LFO3Frequency,
354                      break;                             start_level_mid,
355                  case ::gig::vcf_cutoff_ctrl_genpurpose8:                             lfo3_internal_depth,
356                      VCFCutoffCtrl.controller = 83;                             pRegion->LFO3ControlDepth,
357                      break;                             false,
358                  case ::gig::vcf_cutoff_ctrl_aftertouch: //TODO: not implemented yet                             pEngine->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE);
359                  case ::gig::vcf_cutoff_ctrl_none:              pLFO3->update(pLFO3->ExtController ? GetGigEngineChannel()->ControllerTable[pLFO3->ExtController] : 0);
360                  default:          }
361                      VCFCutoffCtrl.controller = 0;      }
362                      break;      
363              }      float Voice::CalculateCutoffBase(uint8_t MIDIKeyVelocity) {
364              #endif // CONFIG_OVERRIDE_CUTOFF_CTRL          float cutoff = pRegion->GetVelocityCutoff(MIDIKeyVelocity);
365            if (pRegion->VCFKeyboardTracking) {
366              #ifdef CONFIG_OVERRIDE_RESONANCE_CTRL              cutoff *= exp((MIDIKeyVelocity - pRegion->VCFKeyboardTrackingBreakpoint) * 0.057762265f); // (ln(2) / 12)
367              VCFResonanceCtrl.controller = CONFIG_OVERRIDE_RESONANCE_CTRL;          }
368              #else // use the one defined in the instrument file          return cutoff;
369              switch (pDimRgn->VCFResonanceController) {      }
370                  case ::gig::vcf_res_ctrl_genpurpose3:  
371                      VCFResonanceCtrl.controller = 18;      float Voice::CalculateFinalCutoff(float cutoffBase) {
372                      break;          int cvalue;
373                  case ::gig::vcf_res_ctrl_genpurpose4:          if (VCFCutoffCtrl.controller) {
374                      VCFResonanceCtrl.controller = 19;              cvalue = GetGigEngineChannel()->ControllerTable[VCFCutoffCtrl.controller];
375                      break;              if (pRegion->VCFCutoffControllerInvert) cvalue = 127 - cvalue;
376                  case ::gig::vcf_res_ctrl_genpurpose5:              // VCFVelocityScale in this case means Minimum cutoff
377                      VCFResonanceCtrl.controller = 80;              if (cvalue < pRegion->VCFVelocityScale) cvalue = pRegion->VCFVelocityScale;
                     break;  
                 case ::gig::vcf_res_ctrl_genpurpose6:  
                     VCFResonanceCtrl.controller = 81;  
                     break;  
                 case ::gig::vcf_res_ctrl_none:  
                 default:  
                     VCFResonanceCtrl.controller = 0;  
             }  
             #endif // CONFIG_OVERRIDE_RESONANCE_CTRL  
   
             #ifndef CONFIG_OVERRIDE_FILTER_TYPE  
             FilterLeft.SetType(pDimRgn->VCFType);  
             FilterRight.SetType(pDimRgn->VCFType);  
             #else // override filter type  
             FilterLeft.SetType(CONFIG_OVERRIDE_FILTER_TYPE);  
             FilterRight.SetType(CONFIG_OVERRIDE_FILTER_TYPE);  
             #endif // CONFIG_OVERRIDE_FILTER_TYPE  
   
             VCFCutoffCtrl.value    = pEngineChannel->ControllerTable[VCFCutoffCtrl.controller];  
             VCFResonanceCtrl.value = pEngineChannel->ControllerTable[VCFResonanceCtrl.controller];  
   
             // calculate cutoff frequency  
             float cutoff = (!VCFCutoffCtrl.controller)  
                 ? exp((float) (127 - itNoteOnEvent->Param.Note.Velocity) * (float) pDimRgn->VCFVelocityScale * 6.2E-5f * FILTER_CUTOFF_COEFF) * CONFIG_FILTER_CUTOFF_MAX  
                 : exp((float) VCFCutoffCtrl.value * 0.00787402f * FILTER_CUTOFF_COEFF) * CONFIG_FILTER_CUTOFF_MAX;  
   
             // calculate resonance  
             float resonance = (float) VCFResonanceCtrl.value * 0.00787f;   // 0.0..1.0  
             if (pDimRgn->VCFKeyboardTracking) {  
                 resonance += (float) (itNoteOnEvent->Param.Note.Key - pDimRgn->VCFKeyboardTrackingBreakpoint) * 0.00787f;  
             }  
             Constrain(resonance, 0.0, 1.0); // correct resonance if outside allowed value range (0.0..1.0)  
   
             VCFCutoffCtrl.fvalue    = cutoff - CONFIG_FILTER_CUTOFF_MIN;  
             VCFResonanceCtrl.fvalue = resonance;  
   
             FilterUpdateCounter = -1;  
378          }          }
379          else {          else {
380              VCFCutoffCtrl.controller    = 0;              cvalue = pRegion->VCFCutoff;
             VCFResonanceCtrl.controller = 0;  
381          }          }
382            float fco = cutoffBase * float(cvalue);
383            if (fco > 127.0f) fco = 127.0f;
384    
385          return 0; // success          return fco;
386      }      }
387    
388      /**      uint8_t Voice::GetVCFCutoffCtrl() {
389       *  Renders the audio data for this voice for the current audio fragment.          uint8_t ctrl;
390       *  The sample input data can either come from RAM (cached sample or sample          switch (pRegion->VCFCutoffController) {
391       *  part) or directly from disk. The output signal will be rendered by              case ::gig::vcf_cutoff_ctrl_modwheel:
392       *  resampling / interpolation. If this voice is a disk streaming voice and                  ctrl = 1;
      *  the voice completely played back the cached RAM part of the sample, it  
      *  will automatically switch to disk playback for the next RenderAudio()  
      *  call.  
      *  
      *  @param Samples - number of samples to be rendered in this audio fragment cycle  
      */  
     void Voice::Render(uint Samples) {  
   
         // select default values for synthesis mode bits  
         SYNTHESIS_MODE_SET_INTERPOLATE(SynthesisMode, (PitchBase * PitchBend) != 1.0f);  
         SYNTHESIS_MODE_SET_CONSTPITCH(SynthesisMode, true);  
         SYNTHESIS_MODE_SET_LOOP(SynthesisMode, false);  
   
         // Reset the synthesis parameter matrix  
   
         pEngine->ResetSynthesisParameters(Event::destination_vca, this->Volume * this->CrossfadeVolume * pEngineChannel->GlobalVolume);  
         pEngine->ResetSynthesisParameters(Event::destination_vco, this->PitchBase);  
         pEngine->ResetSynthesisParameters(Event::destination_vcfc, VCFCutoffCtrl.fvalue);  
         pEngine->ResetSynthesisParameters(Event::destination_vcfr, VCFResonanceCtrl.fvalue);  
   
         // Apply events to the synthesis parameter matrix  
         ProcessEvents(Samples);  
   
         // Let all modulators write their parameter changes to the synthesis parameter matrix for the current audio fragment  
         pEG1->Process(Samples, pEngineChannel->pMIDIKeyInfo[MIDIKey].pEvents, itTriggerEvent, this->Pos, this->PitchBase * this->PitchBend, itKillEvent);  
         pEG2->Process(Samples, pEngineChannel->pMIDIKeyInfo[MIDIKey].pEvents, itTriggerEvent, this->Pos, this->PitchBase * this->PitchBend);  
         if (pEG3->Process(Samples)) { // if pitch EG is active  
             SYNTHESIS_MODE_SET_INTERPOLATE(SynthesisMode, true);  
             SYNTHESIS_MODE_SET_CONSTPITCH(SynthesisMode, false);  
         }  
         pLFO1->Process(Samples);  
         pLFO2->Process(Samples);  
         if (pLFO3->Process(Samples)) { // if pitch LFO modulation is active  
             SYNTHESIS_MODE_SET_INTERPOLATE(SynthesisMode, true);  
             SYNTHESIS_MODE_SET_CONSTPITCH(SynthesisMode, false);  
         }  
   
         if (SYNTHESIS_MODE_GET_FILTER(SynthesisMode))  
             CalculateBiquadParameters(Samples); // calculate the final biquad filter parameters  
   
         switch (this->PlaybackState) {  
   
             case playback_state_init:  
                 this->PlaybackState = playback_state_ram; // we always start playback from RAM cache and switch then to disk if needed  
                 // no break - continue with playback_state_ram  
   
             case playback_state_ram: {  
                     if (RAMLoop) SYNTHESIS_MODE_SET_LOOP(SynthesisMode, true); // enable looping  
   
                     // render current fragment  
                     Synthesize(Samples, (sample_t*) pSample->GetCache().pStart, Delay);  
   
                     if (DiskVoice) {  
                         // check if we reached the allowed limit of the sample RAM cache  
                         if (Pos > MaxRAMPos) {  
                             dmsg(5,("Voice: switching to disk playback (Pos=%f)\n", Pos));  
                             this->PlaybackState = playback_state_disk;  
                         }  
                     }  
                     else if (Pos >= pSample->GetCache().Size / pSample->FrameSize) {  
                         this->PlaybackState = playback_state_end;  
                     }  
                 }  
                 break;  
   
             case playback_state_disk: {  
                     if (!DiskStreamRef.pStream) {  
                         // check if the disk thread created our ordered disk stream in the meantime  
                         DiskStreamRef.pStream = pDiskThread->AskForCreatedStream(DiskStreamRef.OrderID);  
                         if (!DiskStreamRef.pStream) {  
                             std::cout << stderr << "Disk stream not available in time!" << std::endl << std::flush;  
                             KillImmediately();  
                             return;  
                         }  
                         DiskStreamRef.pStream->IncrementReadPos(pSample->Channels * (int(Pos) - MaxRAMPos));  
                         Pos -= int(Pos);  
                         RealSampleWordsLeftToRead = -1; // -1 means no silence has been added yet  
                     }  
   
                     const int sampleWordsLeftToRead = DiskStreamRef.pStream->GetReadSpace();  
   
                     // add silence sample at the end if we reached the end of the stream (for the interpolator)  
                     if (DiskStreamRef.State == Stream::state_end) {  
                         const int maxSampleWordsPerCycle = (pEngine->MaxSamplesPerCycle << CONFIG_MAX_PITCH) * pSample->Channels + 6; // +6 for the interpolator algorithm  
                         if (sampleWordsLeftToRead <= maxSampleWordsPerCycle) {  
                             // remember how many sample words there are before any silence has been added  
                             if (RealSampleWordsLeftToRead < 0) RealSampleWordsLeftToRead = sampleWordsLeftToRead;  
                             DiskStreamRef.pStream->WriteSilence(maxSampleWordsPerCycle - sampleWordsLeftToRead);  
                         }  
                     }  
   
                     sample_t* ptr = DiskStreamRef.pStream->GetReadPtr(); // get the current read_ptr within the ringbuffer where we read the samples from  
   
                     // render current audio fragment  
                     Synthesize(Samples, ptr, Delay);  
   
                     const int iPos = (int) Pos;  
                     const int readSampleWords = iPos * pSample->Channels; // amount of sample words actually been read  
                     DiskStreamRef.pStream->IncrementReadPos(readSampleWords);  
                     Pos -= iPos; // just keep fractional part of Pos  
   
                     // change state of voice to 'end' if we really reached the end of the sample data  
                     if (RealSampleWordsLeftToRead >= 0) {  
                         RealSampleWordsLeftToRead -= readSampleWords;  
                         if (RealSampleWordsLeftToRead <= 0) this->PlaybackState = playback_state_end;  
                     }  
                 }  
393                  break;                  break;
394                case ::gig::vcf_cutoff_ctrl_effect1:
395              case playback_state_end:                  ctrl = 12;
396                  std::cerr << "gig::Voice::Render(): entered with playback_state_end, this is a bug!\n" << std::flush;                  break;
397                case ::gig::vcf_cutoff_ctrl_effect2:
398                    ctrl = 13;
399                    break;
400                case ::gig::vcf_cutoff_ctrl_breath:
401                    ctrl = 2;
402                    break;
403                case ::gig::vcf_cutoff_ctrl_foot:
404                    ctrl = 4;
405                    break;
406                case ::gig::vcf_cutoff_ctrl_sustainpedal:
407                    ctrl = 64;
408                    break;
409                case ::gig::vcf_cutoff_ctrl_softpedal:
410                    ctrl = 67;
411                    break;
412                case ::gig::vcf_cutoff_ctrl_genpurpose7:
413                    ctrl = 82;
414                    break;
415                case ::gig::vcf_cutoff_ctrl_genpurpose8:
416                    ctrl = 83;
417                    break;
418                case ::gig::vcf_cutoff_ctrl_aftertouch:
419                    ctrl = 128;
420                    break;
421                case ::gig::vcf_cutoff_ctrl_none:
422                default:
423                    ctrl = 0;
424                  break;                  break;
425          }          }
426    
427          // Reset synthesis event lists (except VCO, as VCO events apply channel wide currently)          return ctrl;
         pEngineChannel->pSynthesisEvents[Event::destination_vca]->clear();  
         pEngineChannel->pSynthesisEvents[Event::destination_vcfc]->clear();  
         pEngineChannel->pSynthesisEvents[Event::destination_vcfr]->clear();  
   
         // Reset delay  
         Delay = 0;  
   
         itTriggerEvent = Pool<Event>::Iterator();  
   
         // If sample stream or release stage finished, kill the voice  
         if (PlaybackState == playback_state_end || pEG1->GetStage() == EGADSR::stage_end) KillImmediately();  
     }  
   
     /**  
      *  Resets voice variables. Should only be called if rendering process is  
      *  suspended / not running.  
      */  
     void Voice::Reset() {  
         pLFO1->Reset();  
         pLFO2->Reset();  
         pLFO3->Reset();  
         FilterLeft.Reset();  
         FilterRight.Reset();  
         DiskStreamRef.pStream = NULL;  
         DiskStreamRef.hStream = 0;  
         DiskStreamRef.State   = Stream::state_unused;  
         DiskStreamRef.OrderID = 0;  
         PlaybackState = playback_state_end;  
         itTriggerEvent = Pool<Event>::Iterator();  
         itKillEvent    = Pool<Event>::Iterator();  
     }  
   
     /**  
      *  Process the control change event lists of the engine for the current  
      *  audio fragment. Event values will be applied to the synthesis parameter  
      *  matrix.  
      *  
      *  @param Samples - number of samples to be rendered in this audio fragment cycle  
      */  
     void Voice::ProcessEvents(uint Samples) {  
   
         // dispatch control change events  
         RTList<Event>::Iterator itCCEvent = pEngineChannel->pCCEvents->first();  
         if (Delay) { // skip events that happened before this voice was triggered  
             while (itCCEvent && itCCEvent->FragmentPos() <= Delay) ++itCCEvent;  
         }  
         while (itCCEvent) {  
             if (itCCEvent->Param.CC.Controller) { // if valid MIDI controller  
                 if (itCCEvent->Param.CC.Controller == VCFCutoffCtrl.controller) {  
                     *pEngineChannel->pSynthesisEvents[Event::destination_vcfc]->allocAppend() = *itCCEvent;  
                 }  
                 if (itCCEvent->Param.CC.Controller == VCFResonanceCtrl.controller) {  
                     *pEngineChannel->pSynthesisEvents[Event::destination_vcfr]->allocAppend() = *itCCEvent;  
                 }  
                 if (itCCEvent->Param.CC.Controller == pLFO1->ExtController) {  
                     pLFO1->SendEvent(itCCEvent);  
                 }  
                 if (itCCEvent->Param.CC.Controller == pLFO2->ExtController) {  
                     pLFO2->SendEvent(itCCEvent);  
                 }  
                 if (itCCEvent->Param.CC.Controller == pLFO3->ExtController) {  
                     pLFO3->SendEvent(itCCEvent);  
                 }  
                 if (pDimRgn->AttenuationController.type == ::gig::attenuation_ctrl_t::type_controlchange &&  
                     itCCEvent->Param.CC.Controller == pDimRgn->AttenuationController.controller_number) { // if crossfade event  
                     *pEngineChannel->pSynthesisEvents[Event::destination_vca]->allocAppend() = *itCCEvent;  
                 }  
             }  
   
             ++itCCEvent;  
         }  
   
   
         // process pitch events  
         {  
             RTList<Event>* pVCOEventList = pEngineChannel->pSynthesisEvents[Event::destination_vco];  
             RTList<Event>::Iterator itVCOEvent = pVCOEventList->first();  
             if (Delay) { // skip events that happened before this voice was triggered  
                 while (itVCOEvent && itVCOEvent->FragmentPos() <= Delay) ++itVCOEvent;  
             }  
             // apply old pitchbend value until first pitch event occurs  
             if (this->PitchBend != 1.0) {  
                 uint end = (itVCOEvent) ? itVCOEvent->FragmentPos() : Samples;  
                 for (uint i = Delay; i < end; i++) {  
                     pEngine->pSynthesisParameters[Event::destination_vco][i] *= this->PitchBend;  
                 }  
             }  
             float pitch;  
             while (itVCOEvent) {  
                 RTList<Event>::Iterator itNextVCOEvent = itVCOEvent;  
                 ++itNextVCOEvent;  
   
                 // calculate the influence length of this event (in sample points)  
                 uint end = (itNextVCOEvent) ? itNextVCOEvent->FragmentPos() : Samples;  
   
                 pitch = RTMath::CentsToFreqRatio(((double) itVCOEvent->Param.Pitch.Pitch / 8192.0) * 200.0); // +-two semitones = +-200 cents  
   
                 // apply pitch value to the pitch parameter sequence  
                 for (uint i = itVCOEvent->FragmentPos(); i < end; i++) {  
                     pEngine->pSynthesisParameters[Event::destination_vco][i] *= pitch;  
                 }  
   
                 itVCOEvent = itNextVCOEvent;  
             }  
             if (!pVCOEventList->isEmpty()) {  
                 this->PitchBend = pitch;  
                 SYNTHESIS_MODE_SET_INTERPOLATE(SynthesisMode, true);  
                 SYNTHESIS_MODE_SET_CONSTPITCH(SynthesisMode, false);  
             }  
         }  
   
         // process volume / attenuation events (TODO: we only handle and _expect_ crossfade events here ATM !)  
         {  
             RTList<Event>* pVCAEventList = pEngineChannel->pSynthesisEvents[Event::destination_vca];  
             RTList<Event>::Iterator itVCAEvent = pVCAEventList->first();  
             if (Delay) { // skip events that happened before this voice was triggered  
                 while (itVCAEvent && itVCAEvent->FragmentPos() <= Delay) ++itVCAEvent;  
             }  
             float crossfadevolume;  
             while (itVCAEvent) {  
                 RTList<Event>::Iterator itNextVCAEvent = itVCAEvent;  
                 ++itNextVCAEvent;  
   
                 // calculate the influence length of this event (in sample points)  
                 uint end = (itNextVCAEvent) ? itNextVCAEvent->FragmentPos() : Samples;  
   
                 crossfadevolume = CrossfadeAttenuation(itVCAEvent->Param.CC.Value);  
   
                 float effective_volume = crossfadevolume * this->Volume * pEngineChannel->GlobalVolume;  
   
                 // apply volume value to the volume parameter sequence  
                 for (uint i = itVCAEvent->FragmentPos(); i < end; i++) {  
                     pEngine->pSynthesisParameters[Event::destination_vca][i] = effective_volume;  
                 }  
   
                 itVCAEvent = itNextVCAEvent;  
             }  
             if (!pVCAEventList->isEmpty()) this->CrossfadeVolume = crossfadevolume;  
         }  
   
         // process filter cutoff events  
         {  
             RTList<Event>* pCutoffEventList = pEngineChannel->pSynthesisEvents[Event::destination_vcfc];  
             RTList<Event>::Iterator itCutoffEvent = pCutoffEventList->first();  
             if (Delay) { // skip events that happened before this voice was triggered  
                 while (itCutoffEvent && itCutoffEvent->FragmentPos() <= Delay) ++itCutoffEvent;  
             }  
             float cutoff;  
             while (itCutoffEvent) {  
                 RTList<Event>::Iterator itNextCutoffEvent = itCutoffEvent;  
                 ++itNextCutoffEvent;  
   
                 // calculate the influence length of this event (in sample points)  
                 uint end = (itNextCutoffEvent) ? itNextCutoffEvent->FragmentPos() : Samples;  
   
                 cutoff = exp((float) itCutoffEvent->Param.CC.Value * 0.00787402f * FILTER_CUTOFF_COEFF) * CONFIG_FILTER_CUTOFF_MAX - CONFIG_FILTER_CUTOFF_MIN;  
   
                 // apply cutoff frequency to the cutoff parameter sequence  
                 for (uint i = itCutoffEvent->FragmentPos(); i < end; i++) {  
                     pEngine->pSynthesisParameters[Event::destination_vcfc][i] = cutoff;  
                 }  
   
                 itCutoffEvent = itNextCutoffEvent;  
             }  
             if (!pCutoffEventList->isEmpty()) VCFCutoffCtrl.fvalue = cutoff; // needed for initialization of parameter matrix next time  
         }  
   
         // process filter resonance events  
         {  
             RTList<Event>* pResonanceEventList = pEngineChannel->pSynthesisEvents[Event::destination_vcfr];  
             RTList<Event>::Iterator itResonanceEvent = pResonanceEventList->first();  
             if (Delay) { // skip events that happened before this voice was triggered  
                 while (itResonanceEvent && itResonanceEvent->FragmentPos() <= Delay) ++itResonanceEvent;  
             }  
             while (itResonanceEvent) {  
                 RTList<Event>::Iterator itNextResonanceEvent = itResonanceEvent;  
                 ++itNextResonanceEvent;  
   
                 // calculate the influence length of this event (in sample points)  
                 uint end = (itNextResonanceEvent) ? itNextResonanceEvent->FragmentPos() : Samples;  
   
                 // convert absolute controller value to differential  
                 int ctrldelta = itResonanceEvent->Param.CC.Value - VCFResonanceCtrl.value;  
                 VCFResonanceCtrl.value = itResonanceEvent->Param.CC.Value;  
   
                 float resonancedelta = (float) ctrldelta * 0.00787f; // 0.0..1.0  
   
                 // apply cutoff frequency to the cutoff parameter sequence  
                 for (uint i = itResonanceEvent->FragmentPos(); i < end; i++) {  
                     pEngine->pSynthesisParameters[Event::destination_vcfr][i] += resonancedelta;  
                 }  
   
                 itResonanceEvent = itNextResonanceEvent;  
             }  
             if (!pResonanceEventList->isEmpty()) VCFResonanceCtrl.fvalue = pResonanceEventList->last()->Param.CC.Value * 0.00787f; // needed for initialization of parameter matrix next time  
         }  
     }  
   
     /**  
      * Calculate all necessary, final biquad filter parameters.  
      *  
      * @param Samples - number of samples to be rendered in this audio fragment cycle  
      */  
     void Voice::CalculateBiquadParameters(uint Samples) {  
         biquad_param_t bqbase;  
         biquad_param_t bqmain;  
         float prev_cutoff = pEngine->pSynthesisParameters[Event::destination_vcfc][0];  
         float prev_res    = pEngine->pSynthesisParameters[Event::destination_vcfr][0];  
         FilterLeft.SetParameters( &bqbase, &bqmain, prev_cutoff + CONFIG_FILTER_CUTOFF_MIN, prev_res, pEngine->SampleRate);  
         FilterRight.SetParameters(&bqbase, &bqmain, prev_cutoff + CONFIG_FILTER_CUTOFF_MIN, prev_res, pEngine->SampleRate);  
         pEngine->pBasicFilterParameters[0] = bqbase;  
         pEngine->pMainFilterParameters[0]  = bqmain;  
   
         float* bq;  
         for (int i = 1; i < Samples; i++) {  
             // recalculate biquad parameters if cutoff or resonance differ from previous sample point  
             if (!(i & FILTER_UPDATE_MASK)) {  
                 if (pEngine->pSynthesisParameters[Event::destination_vcfr][i] != prev_res ||  
                     pEngine->pSynthesisParameters[Event::destination_vcfc][i] != prev_cutoff)  
                 {  
                     prev_cutoff = pEngine->pSynthesisParameters[Event::destination_vcfc][i];  
                     prev_res    = pEngine->pSynthesisParameters[Event::destination_vcfr][i];  
                     FilterLeft.SetParameters( &bqbase, &bqmain, prev_cutoff + CONFIG_FILTER_CUTOFF_MIN, prev_res, pEngine->SampleRate);  
                     FilterRight.SetParameters(&bqbase, &bqmain, prev_cutoff + CONFIG_FILTER_CUTOFF_MIN, prev_res, pEngine->SampleRate);  
                 }  
             }  
   
             //same as 'pEngine->pBasicFilterParameters[i] = bqbase;'  
             bq    = (float*) &pEngine->pBasicFilterParameters[i];  
             bq[0] = bqbase.b0;  
             bq[1] = bqbase.b1;  
             bq[2] = bqbase.b2;  
             bq[3] = bqbase.a1;  
             bq[4] = bqbase.a2;  
   
             // same as 'pEngine->pMainFilterParameters[i] = bqmain;'  
             bq    = (float*) &pEngine->pMainFilterParameters[i];  
             bq[0] = bqmain.b0;  
             bq[1] = bqmain.b1;  
             bq[2] = bqmain.b2;  
             bq[3] = bqmain.a1;  
             bq[4] = bqmain.a2;  
         }  
428      }      }
429    
430      /**      uint8_t Voice::GetVCFResonanceCtrl() {
431       *  Synthesizes the current audio fragment for this voice.          uint8_t ctrl;
432       *          switch (pRegion->VCFResonanceController) {
433       *  @param Samples - number of sample points to be rendered in this audio              case ::gig::vcf_res_ctrl_genpurpose3:
434       *                   fragment cycle                  ctrl = 18;
435       *  @param pSrc    - pointer to input sample data                  break;
436       *  @param Skip    - number of sample points to skip in output buffer              case ::gig::vcf_res_ctrl_genpurpose4:
437       */                  ctrl = 19;
438      void Voice::Synthesize(uint Samples, sample_t* pSrc, uint Skip) {                  break;
439          RunSynthesisFunction(SynthesisMode, *this, Samples, pSrc, Skip);              case ::gig::vcf_res_ctrl_genpurpose5:
440      }                  ctrl = 80;
441                    break;
442      /**              case ::gig::vcf_res_ctrl_genpurpose6:
443       *  Immediately kill the voice. This method should not be used to kill                  ctrl = 81;
444       *  a normal, active voice, because it doesn't take care of things like                  break;
445       *  fading down the volume level to avoid clicks and regular processing              case ::gig::vcf_res_ctrl_none:
446       *  until the kill event actually occured!              default:
447       *                  ctrl = 0;
      *  @see Kill()  
      */  
     void Voice::KillImmediately() {  
         if (DiskVoice && DiskStreamRef.State != Stream::state_unused) {  
             pDiskThread->OrderDeletionOfStream(&DiskStreamRef);  
448          }          }
         Reset();  
     }  
   
     /**  
      *  Kill the voice in regular sense. Let the voice render audio until  
      *  the kill event actually occured and then fade down the volume level  
      *  very quickly and let the voice die finally. Unlike a normal release  
      *  of a voice, a kill process cannot be cancalled and is therefore  
      *  usually used for voice stealing and key group conflicts.  
      *  
      *  @param itKillEvent - event which caused the voice to be killed  
      */  
     void Voice::Kill(Pool<Event>::Iterator& itKillEvent) {  
         #if CONFIG_DEVMODE  
         if (!itKillEvent) dmsg(1,("gig::Voice::Kill(): ERROR, !itKillEvent !!!\n"));  
         if (itKillEvent && !itKillEvent.isValid()) dmsg(1,("gig::Voice::Kill(): ERROR, itKillEvent invalid !!!\n"));  
         #endif // CONFIG_DEVMODE  
449    
450          if (itTriggerEvent && itKillEvent->FragmentPos() <= itTriggerEvent->FragmentPos()) return;          return ctrl;
         this->itKillEvent = itKillEvent;  
451      }      }
452    
453  }} // namespace LinuxSampler::gig  }} // namespace LinuxSampler::gig

Legend:
Removed from v.630  
changed lines
  Added in v.2015

  ViewVC Help
Powered by ViewVC