/[svn]/linuxsampler/trunk/src/engines/common/AbstractVoice.cpp
ViewVC logotype

Diff of /linuxsampler/trunk/src/engines/common/AbstractVoice.cpp

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

revision 2032 by persson, Fri Nov 20 20:13:08 2009 UTC revision 2879 by schoenebeck, Tue Apr 19 14:07:53 2016 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-2009 Christian Schoenebeck                         *   *   Copyright (C) 2005-2008 Christian Schoenebeck                         *
7   *   Copyright (C) 2009 Grigor Iliev                                       *   *   Copyright (C) 2009-2015 Christian Schoenebeck and 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 26  Line 26 
26    
27  namespace LinuxSampler {  namespace LinuxSampler {
28    
29      AbstractVoice::AbstractVoice() {      AbstractVoice::AbstractVoice(SignalUnitRack* pRack): pSignalUnitRack(pRack) {
30          pEngineChannel = NULL;          pEngineChannel = NULL;
31          pLFO1 = new LFOUnsigned(1.0f);  // amplitude EG (0..1 range)          pLFO1 = new LFOUnsigned(1.0f);  // amplitude LFO (0..1 range)
32          pLFO2 = new LFOUnsigned(1.0f);  // filter EG (0..1 range)          pLFO2 = new LFOUnsigned(1.0f);  // filter LFO (0..1 range)
33          pLFO3 = new LFOSigned(1200.0f); // pitch EG (-1200..+1200 range)          pLFO3 = new LFOSigned(1200.0f); // pitch LFO (-1200..+1200 range)
34          PlaybackState = playback_state_end;          PlaybackState = playback_state_end;
         KeyGroup = 0;  
35          SynthesisMode = 0; // set all mode bits to 0 first          SynthesisMode = 0; // set all mode bits to 0 first
36          // select synthesis implementation (asm core is not supported ATM)          // select synthesis implementation (asm core is not supported ATM)
37          #if 0 // CONFIG_ASM && ARCH_X86          #if 0 // CONFIG_ASM && ARCH_X86
# Line 44  namespace LinuxSampler { Line 43  namespace LinuxSampler {
43    
44          finalSynthesisParameters.filterLeft.Reset();          finalSynthesisParameters.filterLeft.Reset();
45          finalSynthesisParameters.filterRight.Reset();          finalSynthesisParameters.filterRight.Reset();
46            
47            pEq          = NULL;
48            bEqSupport   = false;
49      }      }
50    
51      AbstractVoice::~AbstractVoice() {      AbstractVoice::~AbstractVoice() {
52          if (pLFO1) delete pLFO1;          if (pLFO1) delete pLFO1;
53          if (pLFO2) delete pLFO2;          if (pLFO2) delete pLFO2;
54          if (pLFO3) delete pLFO3;          if (pLFO3) delete pLFO3;
55            
56            if(pEq != NULL) delete pEq;
57      }      }
58                    
59        void AbstractVoice::CreateEq() {
60            if(!bEqSupport) return;
61            if(pEq != NULL) delete pEq;
62            pEq = new EqSupport;
63            pEq->InitEffect(GetEngine()->pAudioOutputDevice);
64        }
65    
66      /**      /**
67       *  Resets voice variables. Should only be called if rendering process is       *  Resets voice variables. Should only be called if rendering process is
68       *  suspended / not running.       *  suspended / not running.
# Line 99  namespace LinuxSampler { Line 110  namespace LinuxSampler {
110          #endif // CONFIG_DEVMODE          #endif // CONFIG_DEVMODE
111    
112          Type            = VoiceType;          Type            = VoiceType;
113          MIDIKey         = itNoteOnEvent->Param.Note.Key;          pNote           = pEngineChannel->pEngine->NoteByID( itNoteOnEvent->Param.Note.ID );
114          PlaybackState   = playback_state_init; // mark voice as triggered, but no audio rendered yet          PlaybackState   = playback_state_init; // mark voice as triggered, but no audio rendered yet
115          Delay           = itNoteOnEvent->FragmentPos();          Delay           = itNoteOnEvent->FragmentPos();
116          itTriggerEvent  = itNoteOnEvent;          itTriggerEvent  = itNoteOnEvent;
117          itKillEvent     = Pool<Event>::Iterator();          itKillEvent     = Pool<Event>::Iterator();
118          KeyGroup        = iKeyGroup;          MidiKeyBase* pKeyInfo = GetMidiKeyInfo(MIDIKey());
119    
120            pGroupEvents = iKeyGroup ? pEngineChannel->ActiveKeyGroups[iKeyGroup] : 0;
121    
122          SmplInfo   = GetSampleInfo();          SmplInfo   = GetSampleInfo();
123          RgnInfo    = GetRegionInfo();          RgnInfo    = GetRegionInfo();
124          InstrInfo  = GetInstrumentInfo();          InstrInfo  = GetInstrumentInfo();
125            
126            MIDIPan    = CalculatePan(pEngineChannel->iLastPanRequest);
127    
128            AboutToTrigger();
129    
130          // calculate volume          // calculate volume
131          const double velocityAttenuation = GetVelocityAttenuation(itNoteOnEvent->Param.Note.Velocity);          const double velocityAttenuation = GetVelocityAttenuation(itNoteOnEvent->Param.Note.Velocity);
132          float volume = CalculateVolume(velocityAttenuation);          float volume = CalculateVolume(velocityAttenuation) * pKeyInfo->Volume;
133          if (volume <= 0) return -1;          if (volume <= 0) return -1;
134    
135          // select channel mode (mono or stereo)          // select channel mode (mono or stereo)
# Line 123  namespace LinuxSampler { Line 140  namespace LinuxSampler {
140          // get starting crossfade volume level          // get starting crossfade volume level
141          float crossfadeVolume = CalculateCrossfadeVolume(itNoteOnEvent->Param.Note.Velocity);          float crossfadeVolume = CalculateCrossfadeVolume(itNoteOnEvent->Param.Note.Velocity);
142    
143          VolumeLeft  = volume * AbstractEngine::PanCurve[64 - RgnInfo.Pan];          VolumeLeft  = volume * pKeyInfo->PanLeft;
144          VolumeRight = volume * AbstractEngine::PanCurve[64 + RgnInfo.Pan];          VolumeRight = volume * pKeyInfo->PanRight;
145    
146          float subfragmentRate = GetEngine()->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE;          float subfragmentRate = GetEngine()->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE;
147          CrossfadeSmoother.trigger(crossfadeVolume, subfragmentRate);          CrossfadeSmoother.trigger(crossfadeVolume, subfragmentRate);
148          VolumeSmoother.trigger(pEngineChannel->MidiVolume, subfragmentRate);          VolumeSmoother.trigger(pEngineChannel->MidiVolume, subfragmentRate);
         PanLeftSmoother.trigger(pEngineChannel->GlobalPanLeft, subfragmentRate);  
         PanRightSmoother.trigger(pEngineChannel->GlobalPanRight, subfragmentRate);  
   
         finalSynthesisParameters.dPos = RgnInfo.SampleStartOffset; // offset where we should start playback of sample (0 - 2000 sample points)  
         Pos = RgnInfo.SampleStartOffset;  
149    
150          // Check if the sample needs disk streaming or is too short for that          // Check if the sample needs disk streaming or is too short for that
151          long cachedsamples = GetSampleCacheSize() / SmplInfo.FrameSize;          long cachedsamples = GetSampleCacheSize() / SmplInfo.FrameSize;
152          DiskVoice          = cachedsamples < SmplInfo.TotalFrameCount;          DiskVoice          = cachedsamples < SmplInfo.TotalFrameCount;
153    
154            SetSampleStartOffset();
155    
156          if (DiskVoice) { // voice to be streamed from disk          if (DiskVoice) { // voice to be streamed from disk
157              if (cachedsamples > (GetEngine()->MaxSamplesPerCycle << CONFIG_MAX_PITCH)) {              if (cachedsamples > (GetEngine()->MaxSamplesPerCycle << CONFIG_MAX_PITCH)) {
158                  MaxRAMPos = cachedsamples - (GetEngine()->MaxSamplesPerCycle << CONFIG_MAX_PITCH) / SmplInfo.ChannelCount; //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)                  MaxRAMPos = cachedsamples - (GetEngine()->MaxSamplesPerCycle << CONFIG_MAX_PITCH) / SmplInfo.ChannelCount; //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)
# Line 155  namespace LinuxSampler { Line 169  namespace LinuxSampler {
169              RAMLoop = (SmplInfo.HasLoops && (SmplInfo.LoopStart + SmplInfo.LoopLength) <= MaxRAMPos);              RAMLoop = (SmplInfo.HasLoops && (SmplInfo.LoopStart + SmplInfo.LoopLength) <= MaxRAMPos);
170    
171              if (OrderNewStream()) return -1;              if (OrderNewStream()) return -1;
172              dmsg(4,("Disk voice launched (cached samples: %d, total Samples: %d, MaxRAMPos: %d, RAMLooping: %s)\n", cachedsamples, SmplInfo.TotalFrameCount, MaxRAMPos, (RAMLoop) ? "yes" : "no"));              dmsg(4,("Disk voice launched (cached samples: %ld, total Samples: %d, MaxRAMPos: %lu, RAMLooping: %s)\n", cachedsamples, SmplInfo.TotalFrameCount, MaxRAMPos, (RAMLoop) ? "yes" : "no"));
173          }          }
174          else { // RAM only voice          else { // RAM only voice
175              MaxRAMPos = cachedsamples;              MaxRAMPos = cachedsamples;
# Line 175  namespace LinuxSampler { Line 189  namespace LinuxSampler {
189          // the length of the decay and release curves are dependent on the velocity          // the length of the decay and release curves are dependent on the velocity
190          const double velrelease = 1 / GetVelocityRelease(itNoteOnEvent->Param.Note.Velocity);          const double velrelease = 1 / GetVelocityRelease(itNoteOnEvent->Param.Note.Velocity);
191    
192          // setup EG 1 (VCA EG)          if (pSignalUnitRack == NULL) { // setup EG 1 (VCA EG)
         {  
193              // get current value of EG1 controller              // get current value of EG1 controller
194              double eg1controllervalue = GetEG1ControllerValue(itNoteOnEvent->Param.Note.Velocity);              double eg1controllervalue = GetEG1ControllerValue(itNoteOnEvent->Param.Note.Velocity);
195    
196              // calculate influence of EG1 controller on EG1's parameters              // calculate influence of EG1 controller on EG1's parameters
197              EGInfo egInfo = CalculateEG1ControllerInfluence(eg1controllervalue);              EGInfo egInfo = CalculateEG1ControllerInfluence(eg1controllervalue);
198    
199              EG1.trigger (              TriggerEG1(egInfo, velrelease, velocityAttenuation, GetEngine()->SampleRate, itNoteOnEvent->Param.Note.Velocity);
200                  RgnInfo.EG1PreAttack,          } else {
201                  RgnInfo.EG1Attack * egInfo.Attack,              pSignalUnitRack->Trigger();
                 RgnInfo.EG1Hold,  
                 RgnInfo.EG1Decay1 * egInfo.Decay * velrelease,  
                 RgnInfo.EG1Decay2 * egInfo.Decay * velrelease,  
                 RgnInfo.EG1InfiniteSustain,  
                 RgnInfo.EG1Sustain,  
                 RgnInfo.EG1Release * egInfo.Release * velrelease,  
                 velocityAttenuation,  
                 GetEngine()->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE  
             );  
202          }          }
203    
204            uint8_t pan = MIDIPan;
205            if (pSignalUnitRack) pan = pSignalUnitRack->GetEndpointUnit()->CalculatePan(MIDIPan);
206            PanLeftSmoother.trigger(AbstractEngine::PanCurve[128 - pan], subfragmentRate);
207            PanRightSmoother.trigger(AbstractEngine::PanCurve[pan], subfragmentRate);
208    
209  #ifdef CONFIG_INTERPOLATE_VOLUME  #ifdef CONFIG_INTERPOLATE_VOLUME
210          // setup initial volume in synthesis parameters          // setup initial volume in synthesis parameters
211      #ifdef CONFIG_PROCESS_MUTED_CHANNELS      #ifdef CONFIG_PROCESS_MUTED_CHANNELS
# Line 207  namespace LinuxSampler { Line 216  namespace LinuxSampler {
216          else          else
217      #else      #else
218          {          {
219              float finalVolume = pEngineChannel->MidiVolume * crossfadeVolume * EG1.getLevel();              float finalVolume;
220                if (pSignalUnitRack == NULL) {
221                    finalVolume = pEngineChannel->MidiVolume * crossfadeVolume * pEG1->getLevel();
222                } else {
223                    finalVolume = pEngineChannel->MidiVolume * crossfadeVolume * pSignalUnitRack->GetEndpointUnit()->GetVolume();
224                }
225    
226              finalSynthesisParameters.fFinalVolumeLeft  = finalVolume * VolumeLeft  * pEngineChannel->GlobalPanLeft;              finalSynthesisParameters.fFinalVolumeLeft  = finalVolume * VolumeLeft  * PanLeftSmoother.render();
227              finalSynthesisParameters.fFinalVolumeRight = finalVolume * VolumeRight * pEngineChannel->GlobalPanRight;              finalSynthesisParameters.fFinalVolumeRight = finalVolume * VolumeRight * PanRightSmoother.render();
228          }          }
229      #endif      #endif
230  #endif  #endif
231    
232          // setup EG 2 (VCF Cutoff EG)          if (pSignalUnitRack == NULL) {
233          {              // setup EG 2 (VCF Cutoff EG)
234              // get current value of EG2 controller              {
235              double eg2controllervalue = GetEG2ControllerValue(itNoteOnEvent->Param.Note.Velocity);                  // get current value of EG2 controller
236                    double eg2controllervalue = GetEG2ControllerValue(itNoteOnEvent->Param.Note.Velocity);
237    
238                    // calculate influence of EG2 controller on EG2's parameters
239                    EGInfo egInfo = CalculateEG2ControllerInfluence(eg2controllervalue);
240    
241                    TriggerEG2(egInfo, velrelease, velocityAttenuation, GetEngine()->SampleRate, itNoteOnEvent->Param.Note.Velocity);
242                }
243    
             // calculate influence of EG2 controller on EG2's parameters  
             EGInfo egInfo = CalculateEG2ControllerInfluence(eg2controllervalue);  
244    
245              EG2.trigger (              // setup EG 3 (VCO EG)
246                  RgnInfo.EG2PreAttack,              {
247                  RgnInfo.EG2Attack * egInfo.Attack,                  // if portamento mode is on, we dedicate EG3 purely for portamento, otherwise if portamento is off we do as told by the patch
248                  false,                  bool  bPortamento = pEngineChannel->PortamentoMode && pEngineChannel->PortamentoPos >= 0.0f;
249                  RgnInfo.EG2Decay1 * egInfo.Decay * velrelease,                  float eg3depth = (bPortamento)
250                  RgnInfo.EG2Decay2 * egInfo.Decay * velrelease,                               ? RTMath::CentsToFreqRatio((pEngineChannel->PortamentoPos - (float) MIDIKey()) * 100)
251                  RgnInfo.EG2InfiniteSustain,                               : RTMath::CentsToFreqRatio(RgnInfo.EG3Depth);
252                  RgnInfo.EG2Sustain,                  float eg3time = (bPortamento)
253                  RgnInfo.EG2Release * egInfo.Release * velrelease,                              ? pEngineChannel->PortamentoTime
254                  velocityAttenuation,                              : RgnInfo.EG3Attack;
255                  GetEngine()->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE                  EG3.trigger(eg3depth, eg3time, GetEngine()->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE);
256              );                  dmsg(5,("PortamentoPos=%f, depth=%f, time=%f\n", pEngineChannel->PortamentoPos, eg3depth, eg3time));
257          }              }
258    
259    
260          // setup EG 3 (VCO EG)              // setup LFO 1 (VCA LFO)
261          {              InitLFO1();
262              // if portamento mode is on, we dedicate EG3 purely for portamento, otherwise if portamento is off we do as told by the patch              // setup LFO 2 (VCF Cutoff LFO)
263              bool  bPortamento = pEngineChannel->PortamentoMode && pEngineChannel->PortamentoPos >= 0.0f;              InitLFO2();
264              float eg3depth = (bPortamento)              // setup LFO 3 (VCO LFO)
265                           ? RTMath::CentsToFreqRatio((pEngineChannel->PortamentoPos - (float) MIDIKey) * 100)              InitLFO3();
                          : RTMath::CentsToFreqRatio(RgnInfo.EG3Depth);  
             float eg3time = (bPortamento)  
                         ? pEngineChannel->PortamentoTime  
                         : RgnInfo.EG3Attack;  
             EG3.trigger(eg3depth, eg3time, GetEngine()->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE);  
             dmsg(5,("PortamentoPos=%f, depth=%f, time=%f\n", pEngineChannel->PortamentoPos, eg3depth, eg3time));  
266          }          }
267    
268    
         // setup LFO 1 (VCA LFO)  
         InitLFO1();  
         // setup LFO 2 (VCF Cutoff LFO)  
         InitLFO2();  
         // setup LFO 3 (VCO LFO)  
         InitLFO3();  
   
   
269          #if CONFIG_FORCE_FILTER          #if CONFIG_FORCE_FILTER
270          const bool bUseFilter = true;          const bool bUseFilter = true;
271          #else // use filter only if instrument file told so          #else // use filter only if instrument file told so
# Line 303  namespace LinuxSampler { Line 308  namespace LinuxSampler {
308              VCFCutoffCtrl.controller    = 0;              VCFCutoffCtrl.controller    = 0;
309              VCFResonanceCtrl.controller = 0;              VCFResonanceCtrl.controller = 0;
310          }          }
311            
312            const bool bEq =
313                pSignalUnitRack != NULL && pSignalUnitRack->HasEq() && pEq->HasSupport();
314    
315            if (bEq) {
316                pEq->GetInChannelLeft()->Clear();
317                pEq->GetInChannelRight()->Clear();
318                pEq->RenderAudio(GetEngine()->pAudioOutputDevice->MaxSamplesPerCycle());
319            }
320    
321          return 0; // success          return 0; // success
322      }      }
323        
324        void AbstractVoice::SetSampleStartOffset() {
325            finalSynthesisParameters.dPos = RgnInfo.SampleStartOffset; // offset where we should start playback of sample (0 - 2000 sample points)
326            Pos = RgnInfo.SampleStartOffset;
327        }
328    
329      /**      /**
330       *  Synthesizes the current audio fragment for this voice.       *  Synthesizes the current audio fragment for this voice.
# Line 316  namespace LinuxSampler { Line 335  namespace LinuxSampler {
335       *  @param Skip    - number of sample points to skip in output buffer       *  @param Skip    - number of sample points to skip in output buffer
336       */       */
337      void AbstractVoice::Synthesize(uint Samples, sample_t* pSrc, uint Skip) {      void AbstractVoice::Synthesize(uint Samples, sample_t* pSrc, uint Skip) {
338            bool delay = false; // Whether the voice playback should be delayed for this call
339            
340            if (pSignalUnitRack != NULL) {
341                uint delaySteps = pSignalUnitRack->GetEndpointUnit()->DelayTrigger();
342                if (delaySteps > 0) { // delay on the endpoint unit means delay of the voice playback
343                    if (delaySteps >= Samples) {
344                        pSignalUnitRack->GetEndpointUnit()->DecreaseDelay(Samples);
345                        delay = true;
346                    } else {
347                        pSignalUnitRack->GetEndpointUnit()->DecreaseDelay(delaySteps);
348                        Samples -= delaySteps;
349                        Skip += delaySteps;
350                    }
351                }
352            }
353            
354          AbstractEngineChannel* pChannel = pEngineChannel;          AbstractEngineChannel* pChannel = pEngineChannel;
355          finalSynthesisParameters.pOutLeft  = &pChannel->pChannelLeft->Buffer()[Skip];          MidiKeyBase* pMidiKeyInfo = GetMidiKeyInfo(MIDIKey());
356          finalSynthesisParameters.pOutRight = &pChannel->pChannelRight->Buffer()[Skip];  
357          finalSynthesisParameters.pSrc      = pSrc;          const bool bVoiceRequiresDedicatedRouting =
358                pEngineChannel->GetFxSendCount() > 0 &&
359                (pMidiKeyInfo->ReverbSend || pMidiKeyInfo->ChorusSend);
360            
361            const bool bEq =
362                pSignalUnitRack != NULL && pSignalUnitRack->HasEq() && pEq->HasSupport();
363    
364            if (bEq) {
365                pEq->GetInChannelLeft()->Clear();
366                pEq->GetInChannelRight()->Clear();
367                finalSynthesisParameters.pOutLeft  = &pEq->GetInChannelLeft()->Buffer()[Skip];
368                finalSynthesisParameters.pOutRight = &pEq->GetInChannelRight()->Buffer()[Skip];
369                pSignalUnitRack->UpdateEqSettings(pEq);
370            } else if (bVoiceRequiresDedicatedRouting) {
371                finalSynthesisParameters.pOutLeft  = &GetEngine()->pDedicatedVoiceChannelLeft->Buffer()[Skip];
372                finalSynthesisParameters.pOutRight = &GetEngine()->pDedicatedVoiceChannelRight->Buffer()[Skip];
373            } else {
374                finalSynthesisParameters.pOutLeft  = &pChannel->pChannelLeft->Buffer()[Skip];
375                finalSynthesisParameters.pOutRight = &pChannel->pChannelRight->Buffer()[Skip];
376            }
377            finalSynthesisParameters.pSrc = pSrc;
378    
379          RTList<Event>::Iterator itCCEvent = pChannel->pEvents->first();          RTList<Event>::Iterator itCCEvent = pChannel->pEvents->first();
380          RTList<Event>::Iterator itNoteEvent;          RTList<Event>::Iterator itNoteEvent;
381          GetFirstEventOnKey(MIDIKey, itNoteEvent);          GetFirstEventOnKey(HostKey(), itNoteEvent);
382    
383            RTList<Event>::Iterator itGroupEvent;
384            if (pGroupEvents && !Orphan) itGroupEvent = pGroupEvents->first();
385    
386          if (itTriggerEvent) { // skip events that happened before this voice was triggered          if (itTriggerEvent) { // skip events that happened before this voice was triggered
387              while (itCCEvent && itCCEvent->FragmentPos() <= Skip) ++itCCEvent;              while (itCCEvent && itCCEvent->FragmentPos() <= Skip) ++itCCEvent;
388                while (itGroupEvent && itGroupEvent->FragmentPos() <= Skip) ++itGroupEvent;
389    
390              // we can't simply compare the timestamp here, because note events              // we can't simply compare the timestamp here, because note events
391              // might happen on the same time stamp, so we have to deal on the              // might happen on the same time stamp, so we have to deal on the
392              // actual sequence the note events arrived instead (see bug #112)              // actual sequence the note events arrived instead (see bug #112)
# Line 347  namespace LinuxSampler { Line 407  namespace LinuxSampler {
407                  // drivers that use Samples < MaxSamplesPerCycle).                  // drivers that use Samples < MaxSamplesPerCycle).
408                  // End the EG1 here, at pos 0, with a shorter max fade                  // End the EG1 here, at pos 0, with a shorter max fade
409                  // out time.                  // out time.
410                  EG1.enterFadeOutStage(Samples / CONFIG_DEFAULT_SUBFRAGMENT_SIZE);                  if (pSignalUnitRack == NULL) {
411                        pEG1->enterFadeOutStage(Samples / CONFIG_DEFAULT_SUBFRAGMENT_SIZE);
412                    } else {
413                        pSignalUnitRack->EnterFadeOutStage(Samples / CONFIG_DEFAULT_SUBFRAGMENT_SIZE);
414                    }
415                  itKillEvent = Pool<Event>::Iterator();                  itKillEvent = Pool<Event>::Iterator();
416              } else {              } else {
417                  killPos = RTMath::Min(itKillEvent->FragmentPos(), maxFadeOutPos);                  killPos = RTMath::Min(itKillEvent->FragmentPos(), maxFadeOutPos);
# Line 362  namespace LinuxSampler { Line 426  namespace LinuxSampler {
426              fFinalCutoff    = VCFCutoffCtrl.fvalue;              fFinalCutoff    = VCFCutoffCtrl.fvalue;
427              fFinalResonance = VCFResonanceCtrl.fvalue;              fFinalResonance = VCFResonanceCtrl.fvalue;
428    
429              // process MIDI control change and pitchbend events for this subfragment              // process MIDI control change, aftertouch and pitchbend events for this subfragment
430              processCCEvents(itCCEvent, iSubFragmentEnd);              processCCEvents(itCCEvent, iSubFragmentEnd);
431                uint8_t pan = MIDIPan;
432                if (pSignalUnitRack != NULL) pan = pSignalUnitRack->GetEndpointUnit()->CalculatePan(MIDIPan);
433                
434                PanLeftSmoother.update(AbstractEngine::PanCurve[128 - pan]);
435                PanRightSmoother.update(AbstractEngine::PanCurve[pan]);
436    
437              finalSynthesisParameters.fFinalPitch = Pitch.PitchBase * Pitch.PitchBend;              finalSynthesisParameters.fFinalPitch = Pitch.PitchBase * Pitch.PitchBend;
438              float fFinalVolume = VolumeSmoother.render() * CrossfadeSmoother.render();              float fFinalVolume = VolumeSmoother.render() * CrossfadeSmoother.render();
# Line 373  namespace LinuxSampler { Line 442  namespace LinuxSampler {
442    
443              // process transition events (note on, note off & sustain pedal)              // process transition events (note on, note off & sustain pedal)
444              processTransitionEvents(itNoteEvent, iSubFragmentEnd);              processTransitionEvents(itNoteEvent, iSubFragmentEnd);
445                processGroupEvents(itGroupEvent, iSubFragmentEnd);
446                
447                if (pSignalUnitRack == NULL) {
448                    // if the voice was killed in this subfragment, or if the
449                    // filter EG is finished, switch EG1 to fade out stage
450                    if ((itKillEvent && killPos <= iSubFragmentEnd) ||
451                        (SYNTHESIS_MODE_GET_FILTER(SynthesisMode) &&
452                        pEG2->getSegmentType() == EG::segment_end)) {
453                        pEG1->enterFadeOutStage();
454                        itKillEvent = Pool<Event>::Iterator();
455                    }
456    
457              // if the voice was killed in this subfragment, or if the                  // process envelope generators
458              // filter EG is finished, switch EG1 to fade out stage                  switch (pEG1->getSegmentType()) {
459              if ((itKillEvent && killPos <= iSubFragmentEnd) ||                      case EG::segment_lin:
460                  (SYNTHESIS_MODE_GET_FILTER(SynthesisMode) &&                          fFinalVolume *= pEG1->processLin();
461                   EG2.getSegmentType() == gig::EGADSR::segment_end)) {                          break;
462                  EG1.enterFadeOutStage();                      case EG::segment_exp:
463                  itKillEvent = Pool<Event>::Iterator();                          fFinalVolume *= pEG1->processExp();
464              }                          break;
465                        case EG::segment_end:
466              // process envelope generators                          fFinalVolume *= pEG1->getLevel();
467              switch (EG1.getSegmentType()) {                          break; // noop
468                  case gig::EGADSR::segment_lin:                      case EG::segment_pow:
469                      fFinalVolume *= EG1.processLin();                          fFinalVolume *= pEG1->processPow();
470                      break;                          break;
471                  case gig::EGADSR::segment_exp:                  }
472                      fFinalVolume *= EG1.processExp();                  switch (pEG2->getSegmentType()) {
473                      break;                      case EG::segment_lin:
474                  case gig::EGADSR::segment_end:                          fFinalCutoff *= pEG2->processLin();
475                      fFinalVolume *= EG1.getLevel();                          break;
476                      break; // noop                      case EG::segment_exp:
477              }                          fFinalCutoff *= pEG2->processExp();
478              switch (EG2.getSegmentType()) {                          break;
479                  case gig::EGADSR::segment_lin:                      case EG::segment_end:
480                      fFinalCutoff *= EG2.processLin();                          fFinalCutoff *= pEG2->getLevel();
481                      break;                          break; // noop
482                  case gig::EGADSR::segment_exp:                      case EG::segment_pow:
483                      fFinalCutoff *= EG2.processExp();                          fFinalCutoff *= pEG2->processPow();
484                      break;                          break;
485                  case gig::EGADSR::segment_end:                  }
486                      fFinalCutoff *= EG2.getLevel();                  if (EG3.active()) finalSynthesisParameters.fFinalPitch *= EG3.render();
                     break; // noop  
             }  
             if (EG3.active()) finalSynthesisParameters.fFinalPitch *= EG3.render();  
   
             // process low frequency oscillators  
             if (bLFO1Enabled) fFinalVolume *= (1.0f - pLFO1->render());  
             if (bLFO2Enabled) fFinalCutoff *= pLFO2->render();  
             if (bLFO3Enabled) finalSynthesisParameters.fFinalPitch *= RTMath::CentsToFreqRatio(pLFO3->render());  
487    
488                    // process low frequency oscillators
489                    if (bLFO1Enabled) fFinalVolume *= (1.0f - pLFO1->render());
490                    if (bLFO2Enabled) fFinalCutoff *= (1.0f - pLFO2->render());
491                    if (bLFO3Enabled) finalSynthesisParameters.fFinalPitch *= RTMath::CentsToFreqRatio(pLFO3->render());
492                } else {
493                    // if the voice was killed in this subfragment, enter fade out stage
494                    if (itKillEvent && killPos <= iSubFragmentEnd) {
495                        pSignalUnitRack->EnterFadeOutStage();
496                        itKillEvent = Pool<Event>::Iterator();
497                    }
498                    
499                    // if the filter EG is finished, switch EG1 to fade out stage
500                    /*if (SYNTHESIS_MODE_GET_FILTER(SynthesisMode) &&
501                        pEG2->getSegmentType() == EG::segment_end) {
502                        pEG1->enterFadeOutStage();
503                        itKillEvent = Pool<Event>::Iterator();
504                    }*/
505                    // TODO: ^^^
506    
507                    fFinalVolume   *= pSignalUnitRack->GetEndpointUnit()->GetVolume();
508                    fFinalCutoff    = pSignalUnitRack->GetEndpointUnit()->CalculateFilterCutoff(fFinalCutoff);
509                    fFinalResonance = pSignalUnitRack->GetEndpointUnit()->CalculateResonance(fFinalResonance);
510                    
511                    finalSynthesisParameters.fFinalPitch =
512                        pSignalUnitRack->GetEndpointUnit()->CalculatePitch(finalSynthesisParameters.fFinalPitch);
513                        
514                }
515                
516              // limit the pitch so we don't read outside the buffer              // limit the pitch so we don't read outside the buffer
517              finalSynthesisParameters.fFinalPitch = RTMath::Min(finalSynthesisParameters.fFinalPitch, float(1 << CONFIG_MAX_PITCH));              finalSynthesisParameters.fFinalPitch = RTMath::Min(finalSynthesisParameters.fFinalPitch, float(1 << CONFIG_MAX_PITCH));
518    
# Line 445  namespace LinuxSampler { Line 545  namespace LinuxSampler {
545                  fFinalVolume * VolumeRight * PanRightSmoother.render();                  fFinalVolume * VolumeRight * PanRightSmoother.render();
546  #endif  #endif
547              // render audio for one subfragment              // render audio for one subfragment
548              RunSynthesisFunction(SynthesisMode, &finalSynthesisParameters, &loop);              if (!delay) RunSynthesisFunction(SynthesisMode, &finalSynthesisParameters, &loop);
549    
550              // stop the rendering if volume EG is finished              if (pSignalUnitRack == NULL) {
551              if (EG1.getSegmentType() == gig::EGADSR::segment_end) break;                  // stop the rendering if volume EG is finished
552                    if (pEG1->getSegmentType() == EG::segment_end) break;
553                } else {
554                    // stop the rendering if the endpoint unit is not active
555                    if (!pSignalUnitRack->GetEndpointUnit()->Active()) break;
556                }
557    
558              const double newPos = Pos + (iSubFragmentEnd - i) * finalSynthesisParameters.fFinalPitch;              const double newPos = Pos + (iSubFragmentEnd - i) * finalSynthesisParameters.fFinalPitch;
559    
560              // increment envelopes' positions              if (pSignalUnitRack == NULL) {
561              if (EG1.active()) {                  // increment envelopes' positions
562                    if (pEG1->active()) {
563    
564                        // if sample has a loop and loop start has been reached in this subfragment, send a special event to EG1 to let it finish the attack hold stage
565                        if (SmplInfo.HasLoops && Pos <= SmplInfo.LoopStart && SmplInfo.LoopStart < newPos) {
566                            pEG1->update(EG::event_hold_end, GetEngine()->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE);
567                        }
568    
569                  // if sample has a loop and loop start has been reached in this subfragment, send a special event to EG1 to let it finish the attack hold stage                      pEG1->increment(1);
570                  if (SmplInfo.HasLoops && Pos <= SmplInfo.LoopStart && SmplInfo.LoopStart < newPos) {                      if (!pEG1->toStageEndLeft()) pEG1->update(EG::event_stage_end, GetEngine()->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE);
                     EG1.update(gig::EGADSR::event_hold_end, GetEngine()->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE);  
571                  }                  }
572                    if (pEG2->active()) {
573                  EG1.increment(1);                      pEG2->increment(1);
574                  if (!EG1.toStageEndLeft()) EG1.update(gig::EGADSR::event_stage_end, GetEngine()->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE);                      if (!pEG2->toStageEndLeft()) pEG2->update(EG::event_stage_end, GetEngine()->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE);
575              }                  }
576              if (EG2.active()) {                  EG3.increment(1);
577                  EG2.increment(1);                  if (!EG3.toEndLeft()) EG3.update(); // neutralize envelope coefficient if end reached
578                  if (!EG2.toStageEndLeft()) EG2.update(gig::EGADSR::event_stage_end, GetEngine()->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE);              } else {
579                        // if sample has a loop and loop start has been reached in this subfragment, send a special event to EG1 to let it finish the attack hold stage
580                        /*if (SmplInfo.HasLoops && Pos <= SmplInfo.LoopStart && SmplInfo.LoopStart < newPos) {
581                            pEG1->update(EG::event_hold_end, GetEngine()->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE);
582                        }*/
583                    // TODO: ^^^
584                    
585                    if (!delay) pSignalUnitRack->Increment();
586              }              }
             EG3.increment(1);  
             if (!EG3.toEndLeft()) EG3.update(); // neutralize envelope coefficient if end reached  
587    
588              Pos = newPos;              Pos = newPos;
589              i = iSubFragmentEnd;              i = iSubFragmentEnd;
590          }          }
591            
592            if (delay) return;
593    
594            if (bVoiceRequiresDedicatedRouting) {
595                if (bEq) {
596                    pEq->RenderAudio(Samples);
597                    pEq->GetOutChannelLeft()->CopyTo(GetEngine()->pDedicatedVoiceChannelLeft, Samples);
598                    pEq->GetOutChannelRight()->CopyTo(GetEngine()->pDedicatedVoiceChannelRight, Samples);
599                }
600                optional<float> effectSendLevels[2] = {
601                    pMidiKeyInfo->ReverbSend,
602                    pMidiKeyInfo->ChorusSend
603                };
604                GetEngine()->RouteDedicatedVoiceChannels(pEngineChannel, effectSendLevels, Samples);
605            } else if (bEq) {
606                pEq->RenderAudio(Samples);
607                pEq->GetOutChannelLeft()->MixTo(pChannel->pChannelLeft, Samples);
608                pEq->GetOutChannelRight()->MixTo(pChannel->pChannelRight, Samples);
609            }
610      }      }
611        
612      /**      /**
613       * Process given list of MIDI control change and pitch bend events for       * Process given list of MIDI control change, aftertouch and pitch bend
614       * the given time.       * events for the given time.
615       *       *
616       * @param itEvent - iterator pointing to the next event to be processed       * @param itEvent - iterator pointing to the next event to be processed
617       * @param End     - youngest time stamp where processing should be stopped       * @param End     - youngest time stamp where processing should be stopped
# Line 491  namespace LinuxSampler { Line 625  namespace LinuxSampler {
625                  if (itEvent->Param.CC.Controller == VCFResonanceCtrl.controller) {                  if (itEvent->Param.CC.Controller == VCFResonanceCtrl.controller) {
626                      processResonanceEvent(itEvent);                      processResonanceEvent(itEvent);
627                  }                  }
628                  if (itEvent->Param.CC.Controller == pLFO1->ExtController) {                  if (pSignalUnitRack == NULL) {
629                      pLFO1->update(itEvent->Param.CC.Value);                      if (itEvent->Param.CC.Controller == pLFO1->ExtController) {
630                  }                          pLFO1->update(itEvent->Param.CC.Value);
631                  if (itEvent->Param.CC.Controller == pLFO2->ExtController) {                      }
632                      pLFO2->update(itEvent->Param.CC.Value);                      if (itEvent->Param.CC.Controller == pLFO2->ExtController) {
633                  }                          pLFO2->update(itEvent->Param.CC.Value);
634                  if (itEvent->Param.CC.Controller == pLFO3->ExtController) {                      }
635                      pLFO3->update(itEvent->Param.CC.Value);                      if (itEvent->Param.CC.Controller == pLFO3->ExtController) {
636                            pLFO3->update(itEvent->Param.CC.Value);
637                        }
638                  }                  }
639                  if (itEvent->Param.CC.Controller == 7) { // volume                  if (itEvent->Param.CC.Controller == 7) { // volume
640                      VolumeSmoother.update(AbstractEngine::VolumeCurve[itEvent->Param.CC.Value]);                      VolumeSmoother.update(AbstractEngine::VolumeCurve[itEvent->Param.CC.Value]);
641                  } else if (itEvent->Param.CC.Controller == 10) { // panpot                  } else if (itEvent->Param.CC.Controller == 10) { // panpot
642                      PanLeftSmoother.update(AbstractEngine::PanCurve[128 - itEvent->Param.CC.Value]);                      MIDIPan = CalculatePan(itEvent->Param.CC.Value);
                     PanRightSmoother.update(AbstractEngine::PanCurve[itEvent->Param.CC.Value]);  
643                  }                  }
644              } else if (itEvent->Type == Event::type_pitchbend) { // if pitch bend event              } else if (itEvent->Type == Event::type_pitchbend) { // if pitch bend event
645                  processPitchEvent(itEvent);                  processPitchEvent(itEvent);
646                } else if (itEvent->Type == Event::type_channel_pressure) {
647                    ProcessChannelPressureEvent(itEvent);
648                } else if (itEvent->Type == Event::type_note_pressure) {
649                    ProcessPolyphonicKeyPressureEvent(itEvent);
650              }              }
651    
652              ProcessCCEvent(itEvent);              ProcessCCEvent(itEvent);
653                if (pSignalUnitRack != NULL) {
654                    pSignalUnitRack->ProcessCCEvent(itEvent);
655                }
656          }          }
657      }      }
658    
# Line 537  namespace LinuxSampler { Line 679  namespace LinuxSampler {
679       */       */
680      void AbstractVoice::processTransitionEvents(RTList<Event>::Iterator& itEvent, uint End) {      void AbstractVoice::processTransitionEvents(RTList<Event>::Iterator& itEvent, uint End) {
681          for (; itEvent && itEvent->FragmentPos() <= End; ++itEvent) {          for (; itEvent && itEvent->FragmentPos() <= End; ++itEvent) {
682              if (itEvent->Type == Event::type_release) {              // some voice types ignore note off
683                  EG1.update(gig::EGADSR::event_release, GetEngine()->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE);              if (!(Type & (Voice::type_one_shot | Voice::type_release_trigger | Voice::type_controller_triggered))) {
684                  EG2.update(gig::EGADSR::event_release, GetEngine()->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE);                  if (itEvent->Type == Event::type_release) {
685              } else if (itEvent->Type == Event::type_cancel_release) {                      EnterReleaseStage();
686                  EG1.update(gig::EGADSR::event_cancel_release, GetEngine()->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE);                  } else if (itEvent->Type == Event::type_cancel_release) {
687                  EG2.update(gig::EGADSR::event_cancel_release, GetEngine()->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE);                      if (pSignalUnitRack == NULL) {
688                            pEG1->update(EG::event_cancel_release, GetEngine()->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE);
689                            pEG2->update(EG::event_cancel_release, GetEngine()->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE);
690                        } else {
691                            pSignalUnitRack->CancelRelease();
692                        }
693                    }
694              }              }
695          }          }
696      }      }
697    
698        /**
699         * Process given list of events aimed at all voices in a key group.
700         *
701         * @param itEvent - iterator pointing to the next event to be processed
702         * @param End     - youngest time stamp where processing should be stopped
703         */
704        void AbstractVoice::processGroupEvents(RTList<Event>::Iterator& itEvent, uint End) {
705            for (; itEvent && itEvent->FragmentPos() <= End; ++itEvent) {
706                ProcessGroupEvent(itEvent);
707            }
708        }
709    
710      /** @brief Update current portamento position.      /** @brief Update current portamento position.
711       *       *
712       * Will be called when portamento mode is enabled to get the final       * Will be called when portamento mode is enabled to get the final
# Line 556  namespace LinuxSampler { Line 716  namespace LinuxSampler {
716       * @param itNoteOffEvent - event which causes this voice to die soon       * @param itNoteOffEvent - event which causes this voice to die soon
717       */       */
718      void AbstractVoice::UpdatePortamentoPos(Pool<Event>::Iterator& itNoteOffEvent) {      void AbstractVoice::UpdatePortamentoPos(Pool<Event>::Iterator& itNoteOffEvent) {
719          const float fFinalEG3Level = EG3.level(itNoteOffEvent->FragmentPos());          if (pSignalUnitRack == NULL) {
720          pEngineChannel->PortamentoPos = (float) MIDIKey + RTMath::FreqRatioToCents(fFinalEG3Level) * 0.01f;              const float fFinalEG3Level = EG3.level(itNoteOffEvent->FragmentPos());
721                pEngineChannel->PortamentoPos = (float) MIDIKey() + RTMath::FreqRatioToCents(fFinalEG3Level) * 0.01f;
722            } else {
723                // TODO:
724            }
725      }      }
726    
727      /**      /**
# Line 581  namespace LinuxSampler { Line 745  namespace LinuxSampler {
745    
746      Voice::PitchInfo AbstractVoice::CalculatePitchInfo(int PitchBend) {      Voice::PitchInfo AbstractVoice::CalculatePitchInfo(int PitchBend) {
747          PitchInfo pitch;          PitchInfo pitch;
748          double pitchbasecents = InstrInfo.FineTune + RgnInfo.FineTune + GetEngine()->ScaleTuning[MIDIKey % 12];          double pitchbasecents = InstrInfo.FineTune + RgnInfo.FineTune + GetEngine()->ScaleTuning[MIDIKey() % 12];
749    
750          // GSt behaviour: maximum transpose up is 40 semitones. If          // GSt behaviour: maximum transpose up is 40 semitones. If
751          // MIDI key is more than 40 semitones above unity note,          // MIDI key is more than 40 semitones above unity note,
752          // the transpose is not done.          // the transpose is not done.
753          if (!SmplInfo.Unpitched && (MIDIKey - (int) RgnInfo.UnityNote) < 40) pitchbasecents += (MIDIKey - (int) RgnInfo.UnityNote) * 100;          if (!SmplInfo.Unpitched && (MIDIKey() - (int) RgnInfo.UnityNote) < 40) pitchbasecents += (MIDIKey() - (int) RgnInfo.UnityNote) * 100;
754    
755          pitch.PitchBase = RTMath::CentsToFreqRatioUnlimited(pitchbasecents) * (double(SmplInfo.SampleRate) / double(GetEngine()->SampleRate));          pitch.PitchBase = RTMath::CentsToFreqRatioUnlimited(pitchbasecents) * (double(SmplInfo.SampleRate) / double(GetEngine()->SampleRate));
756          pitch.PitchBendRange = 1.0 / 8192.0 * 100.0 * InstrInfo.PitchbendRange;          pitch.PitchBendRange = 1.0 / 8192.0 * 100.0 * InstrInfo.PitchbendRange;
# Line 594  namespace LinuxSampler { Line 758  namespace LinuxSampler {
758    
759          return pitch;          return pitch;
760      }      }
761        
762        void AbstractVoice::onScaleTuningChanged() {
763            PitchInfo pitch = this->Pitch;
764            double pitchbasecents = InstrInfo.FineTune + RgnInfo.FineTune + GetEngine()->ScaleTuning[MIDIKey() % 12];
765            
766            // GSt behaviour: maximum transpose up is 40 semitones. If
767            // MIDI key is more than 40 semitones above unity note,
768            // the transpose is not done.
769            if (!SmplInfo.Unpitched && (MIDIKey() - (int) RgnInfo.UnityNote) < 40) pitchbasecents += (MIDIKey() - (int) RgnInfo.UnityNote) * 100;
770            
771            pitch.PitchBase = RTMath::CentsToFreqRatioUnlimited(pitchbasecents) * (double(SmplInfo.SampleRate) / double(GetEngine()->SampleRate));
772            this->Pitch = pitch;
773        }
774    
775      double AbstractVoice::CalculateVolume(double velocityAttenuation) {      double AbstractVoice::CalculateVolume(double velocityAttenuation) {
776          // For 16 bit samples, we downscale by 32768 to convert from          // For 16 bit samples, we downscale by 32768 to convert from
# Line 604  namespace LinuxSampler { Line 781  namespace LinuxSampler {
781          volume *= GetSampleAttenuation() * pEngineChannel->GlobalVolume * GLOBAL_VOLUME;          volume *= GetSampleAttenuation() * pEngineChannel->GlobalVolume * GLOBAL_VOLUME;
782    
783          // the volume of release triggered samples depends on note length          // the volume of release triggered samples depends on note length
784          if (Type == Voice::type_release_trigger) {          if (Type & Voice::type_release_trigger) {
785              float noteLength = float(GetEngine()->FrameTime + Delay -              float noteLength = float(GetEngine()->FrameTime + Delay -
786                  GetNoteOnTime(MIDIKey) ) / GetEngine()->SampleRate;                  GetNoteOnTime(MIDIKey()) ) / GetEngine()->SampleRate;
787    
788              float attenuation = 1 - 0.01053 * (256 >> RgnInfo.ReleaseTriggerDecay) * noteLength;              volume *= GetReleaseTriggerAttenuation(noteLength);
             volume *= attenuation;  
789          }          }
790    
791          return volume;          return volume;
792      }      }
793    
794        float AbstractVoice::GetReleaseTriggerAttenuation(float noteLength) {
795            return 1 - RgnInfo.ReleaseTriggerDecay * noteLength;
796        }
797    
798        void AbstractVoice::EnterReleaseStage() {
799            if (pSignalUnitRack == NULL) {
800                pEG1->update(EG::event_release, GetEngine()->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE);
801                pEG2->update(EG::event_release, GetEngine()->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE);
802            } else {
803                pSignalUnitRack->EnterReleaseStage();
804            }
805        }
806    
807        bool AbstractVoice::EG1Finished() {
808            if (pSignalUnitRack == NULL) {
809                return pEG1->getSegmentType() == EG::segment_end;
810            } else {
811                return !pSignalUnitRack->GetEndpointUnit()->Active();
812            }
813        }
814    
815  } // namespace LinuxSampler  } // namespace LinuxSampler

Legend:
Removed from v.2032  
changed lines
  Added in v.2879

  ViewVC Help
Powered by ViewVC