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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3054 - (hide annotations) (download)
Thu Dec 15 12:47:45 2016 UTC (7 years, 4 months ago) by schoenebeck
File size: 42892 byte(s)
* Fixed numerous compiler warnings.
* Bumped version (2.0.0.svn32).

1 iliev 2015 /***************************************************************************
2     * *
3     * LinuxSampler - modular, streaming capable sampler *
4     * *
5     * Copyright (C) 2003,2004 by Benno Senoner and Christian Schoenebeck *
6 persson 2045 * Copyright (C) 2005-2008 Christian Schoenebeck *
7 schoenebeck 3054 * Copyright (C) 2009-2012 Christian Schoenebeck and Grigor Iliev *
8     * Copyright (C) 2013-2016 Christian Schoenebeck and Andreas Persson *
9 iliev 2015 * *
10     * This program is free software; you can redistribute it and/or modify *
11     * it under the terms of the GNU General Public License as published by *
12     * the Free Software Foundation; either version 2 of the License, or *
13     * (at your option) any later version. *
14     * *
15     * This program is distributed in the hope that it will be useful, *
16     * but WITHOUT ANY WARRANTY; without even the implied warranty of *
17     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
18     * GNU General Public License for more details. *
19     * *
20     * You should have received a copy of the GNU General Public License *
21     * along with this program; if not, write to the Free Software *
22     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, *
23     * MA 02111-1307 USA *
24     ***************************************************************************/
25    
26     #include "AbstractVoice.h"
27    
28     namespace LinuxSampler {
29    
30 iliev 2217 AbstractVoice::AbstractVoice(SignalUnitRack* pRack): pSignalUnitRack(pRack) {
31 iliev 2015 pEngineChannel = NULL;
32 persson 2175 pLFO1 = new LFOUnsigned(1.0f); // amplitude LFO (0..1 range)
33     pLFO2 = new LFOUnsigned(1.0f); // filter LFO (0..1 range)
34     pLFO3 = new LFOSigned(1200.0f); // pitch LFO (-1200..+1200 range)
35 iliev 2015 PlaybackState = playback_state_end;
36     SynthesisMode = 0; // set all mode bits to 0 first
37     // select synthesis implementation (asm core is not supported ATM)
38     #if 0 // CONFIG_ASM && ARCH_X86
39     SYNTHESIS_MODE_SET_IMPLEMENTATION(SynthesisMode, Features::supportsMMX() && Features::supportsSSE());
40     #else
41     SYNTHESIS_MODE_SET_IMPLEMENTATION(SynthesisMode, false);
42     #endif
43     SYNTHESIS_MODE_SET_PROFILING(SynthesisMode, gig::Profiler::isEnabled());
44    
45     finalSynthesisParameters.filterLeft.Reset();
46     finalSynthesisParameters.filterRight.Reset();
47 iliev 2298
48     pEq = NULL;
49     bEqSupport = false;
50 iliev 2015 }
51    
52     AbstractVoice::~AbstractVoice() {
53     if (pLFO1) delete pLFO1;
54     if (pLFO2) delete pLFO2;
55     if (pLFO3) delete pLFO3;
56 iliev 2298
57     if(pEq != NULL) delete pEq;
58 iliev 2015 }
59 iliev 2298
60     void AbstractVoice::CreateEq() {
61     if(!bEqSupport) return;
62     if(pEq != NULL) delete pEq;
63     pEq = new EqSupport;
64     pEq->InitEffect(GetEngine()->pAudioOutputDevice);
65     }
66 persson 2045
67 iliev 2015 /**
68     * Resets voice variables. Should only be called if rendering process is
69     * suspended / not running.
70     */
71     void AbstractVoice::Reset() {
72     finalSynthesisParameters.filterLeft.Reset();
73     finalSynthesisParameters.filterRight.Reset();
74     DiskStreamRef.pStream = NULL;
75     DiskStreamRef.hStream = 0;
76     DiskStreamRef.State = Stream::state_unused;
77     DiskStreamRef.OrderID = 0;
78     PlaybackState = playback_state_end;
79     itTriggerEvent = Pool<Event>::Iterator();
80     itKillEvent = Pool<Event>::Iterator();
81     }
82    
83     /**
84     * Initializes and triggers the voice, a disk stream will be launched if
85     * needed.
86     *
87     * @param pEngineChannel - engine channel on which this voice was ordered
88     * @param itNoteOnEvent - event that caused triggering of this voice
89     * @param PitchBend - MIDI detune factor (-8192 ... +8191)
90     * @param pRegion- points to the region which provides sample wave(s) and articulation data
91     * @param VoiceType - type of this voice
92     * @param iKeyGroup - a value > 0 defines a key group in which this voice is member of
93     * @returns 0 on success, a value < 0 if the voice wasn't triggered
94     * (either due to an error or e.g. because no region is
95     * defined for the given key)
96     */
97     int AbstractVoice::Trigger (
98     AbstractEngineChannel* pEngineChannel,
99     Pool<Event>::Iterator& itNoteOnEvent,
100     int PitchBend,
101     type_t VoiceType,
102     int iKeyGroup
103     ) {
104     this->pEngineChannel = pEngineChannel;
105     Orphan = false;
106    
107     #if CONFIG_DEVMODE
108     if (itNoteOnEvent->FragmentPos() > GetEngine()->MaxSamplesPerCycle) { // just a sanity check for debugging
109     dmsg(1,("Voice::Trigger(): ERROR, TriggerDelay > Totalsamples\n"));
110     }
111     #endif // CONFIG_DEVMODE
112    
113     Type = VoiceType;
114 schoenebeck 2879 pNote = pEngineChannel->pEngine->NoteByID( itNoteOnEvent->Param.Note.ID );
115 iliev 2015 PlaybackState = playback_state_init; // mark voice as triggered, but no audio rendered yet
116     Delay = itNoteOnEvent->FragmentPos();
117     itTriggerEvent = itNoteOnEvent;
118     itKillEvent = Pool<Event>::Iterator();
119 schoenebeck 2879 MidiKeyBase* pKeyInfo = GetMidiKeyInfo(MIDIKey());
120 iliev 2015
121 persson 2114 pGroupEvents = iKeyGroup ? pEngineChannel->ActiveKeyGroups[iKeyGroup] : 0;
122    
123 iliev 2015 SmplInfo = GetSampleInfo();
124     RgnInfo = GetRegionInfo();
125     InstrInfo = GetInstrumentInfo();
126 iliev 2205
127 persson 2382 MIDIPan = CalculatePan(pEngineChannel->iLastPanRequest);
128    
129 iliev 2205 AboutToTrigger();
130 iliev 2015
131     // calculate volume
132     const double velocityAttenuation = GetVelocityAttenuation(itNoteOnEvent->Param.Note.Velocity);
133 schoenebeck 2121 float volume = CalculateVolume(velocityAttenuation) * pKeyInfo->Volume;
134 persson 2032 if (volume <= 0) return -1;
135 iliev 2015
136     // select channel mode (mono or stereo)
137     SYNTHESIS_MODE_SET_CHANNELS(SynthesisMode, SmplInfo.ChannelCount == 2);
138     // select bit depth (16 or 24)
139     SYNTHESIS_MODE_SET_BITDEPTH24(SynthesisMode, SmplInfo.BitDepth == 24);
140    
141     // get starting crossfade volume level
142     float crossfadeVolume = CalculateCrossfadeVolume(itNoteOnEvent->Param.Note.Velocity);
143    
144 persson 2382 VolumeLeft = volume * pKeyInfo->PanLeft;
145     VolumeRight = volume * pKeyInfo->PanRight;
146 iliev 2015
147 schoenebeck 2963 // this rate is used for rather mellow volume fades
148     const float subfragmentRate = GetEngine()->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE;
149     // this rate is used for very fast volume fades
150     const float quickRampRate = RTMath::Min(subfragmentRate, GetEngine()->SampleRate * 0.001f /* 1ms */);
151 iliev 2015 CrossfadeSmoother.trigger(crossfadeVolume, subfragmentRate);
152 schoenebeck 2963
153 iliev 2015 VolumeSmoother.trigger(pEngineChannel->MidiVolume, subfragmentRate);
154 schoenebeck 2963 NoteVolumeSmoother.trigger(pNote ? pNote->Override.Volume : 1.f, quickRampRate);
155 iliev 2015
156     // Check if the sample needs disk streaming or is too short for that
157     long cachedsamples = GetSampleCacheSize() / SmplInfo.FrameSize;
158     DiskVoice = cachedsamples < SmplInfo.TotalFrameCount;
159    
160 iliev 2216 SetSampleStartOffset();
161    
162 iliev 2015 if (DiskVoice) { // voice to be streamed from disk
163     if (cachedsamples > (GetEngine()->MaxSamplesPerCycle << CONFIG_MAX_PITCH)) {
164     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)
165     } else {
166     // The cache is too small to fit a max sample buffer.
167     // Setting MaxRAMPos to 0 will probably cause a click
168     // in the audio, but it's better than not handling
169     // this case at all, which would have caused the
170     // unsigned MaxRAMPos to be set to a negative number.
171     MaxRAMPos = 0;
172     }
173    
174     // check if there's a loop defined which completely fits into the cached (RAM) part of the sample
175     RAMLoop = (SmplInfo.HasLoops && (SmplInfo.LoopStart + SmplInfo.LoopLength) <= MaxRAMPos);
176    
177     if (OrderNewStream()) return -1;
178 persson 2837 dmsg(4,("Disk voice launched (cached samples: %ld, total Samples: %d, MaxRAMPos: %lu, RAMLooping: %s)\n", cachedsamples, SmplInfo.TotalFrameCount, MaxRAMPos, (RAMLoop) ? "yes" : "no"));
179 iliev 2015 }
180     else { // RAM only voice
181     MaxRAMPos = cachedsamples;
182     RAMLoop = (SmplInfo.HasLoops);
183     dmsg(4,("RAM only voice launched (Looping: %s)\n", (RAMLoop) ? "yes" : "no"));
184     }
185     if (RAMLoop) {
186     loop.uiTotalCycles = SmplInfo.LoopPlayCount;
187     loop.uiCyclesLeft = SmplInfo.LoopPlayCount;
188     loop.uiStart = SmplInfo.LoopStart;
189     loop.uiEnd = SmplInfo.LoopStart + SmplInfo.LoopLength;
190     loop.uiSize = SmplInfo.LoopLength;
191     }
192    
193     Pitch = CalculatePitchInfo(PitchBend);
194 schoenebeck 2931 NotePitch = (pNote) ? pNote->Override.Pitch : 1.0f;
195 schoenebeck 2935 NoteCutoff = (pNote) ? pNote->Override.Cutoff : 1.0f;
196     NoteResonance = (pNote) ? pNote->Override.Resonance : 1.0f;
197 iliev 2015
198     // the length of the decay and release curves are dependent on the velocity
199     const double velrelease = 1 / GetVelocityRelease(itNoteOnEvent->Param.Note.Velocity);
200    
201 iliev 2217 if (pSignalUnitRack == NULL) { // setup EG 1 (VCA EG)
202 iliev 2015 // get current value of EG1 controller
203     double eg1controllervalue = GetEG1ControllerValue(itNoteOnEvent->Param.Note.Velocity);
204    
205     // calculate influence of EG1 controller on EG1's parameters
206     EGInfo egInfo = CalculateEG1ControllerInfluence(eg1controllervalue);
207    
208 schoenebeck 2953 if (pNote) {
209     egInfo.Attack *= pNote->Override.Attack;
210     egInfo.Decay *= pNote->Override.Decay;
211     egInfo.Release *= pNote->Override.Release;
212     }
213    
214 persson 2055 TriggerEG1(egInfo, velrelease, velocityAttenuation, GetEngine()->SampleRate, itNoteOnEvent->Param.Note.Velocity);
215 iliev 2205 } else {
216 iliev 2217 pSignalUnitRack->Trigger();
217 iliev 2015 }
218    
219 schoenebeck 2931 const uint8_t pan = (pSignalUnitRack) ? pSignalUnitRack->GetEndpointUnit()->CalculatePan(MIDIPan) : MIDIPan;
220     NotePanLeft = (pNote) ? AbstractEngine::PanCurveValueNorm(pNote->Override.Pan, 0 /*left*/ ) : 1.f;
221     NotePanRight = (pNote) ? AbstractEngine::PanCurveValueNorm(pNote->Override.Pan, 1 /*right*/) : 1.f;
222     PanLeftSmoother.trigger(
223     AbstractEngine::PanCurve[128 - pan] * NotePanLeft,
224 schoenebeck 2963 quickRampRate //NOTE: maybe we should have 2 separate pan smoothers, one for MIDI CC10 (with slow rate) and one for instrument script change_pan() calls (with fast rate)
225 schoenebeck 2931 );
226     PanRightSmoother.trigger(
227     AbstractEngine::PanCurve[pan] * NotePanRight,
228 schoenebeck 2963 quickRampRate //NOTE: maybe we should have 2 separate pan smoothers, one for MIDI CC10 (with slow rate) and one for instrument script change_pan() calls (with fast rate)
229 schoenebeck 2931 );
230 persson 2382
231 iliev 2015 #ifdef CONFIG_INTERPOLATE_VOLUME
232     // setup initial volume in synthesis parameters
233     #ifdef CONFIG_PROCESS_MUTED_CHANNELS
234     if (pEngineChannel->GetMute()) {
235     finalSynthesisParameters.fFinalVolumeLeft = 0;
236     finalSynthesisParameters.fFinalVolumeRight = 0;
237     }
238     else
239     #else
240     {
241 iliev 2205 float finalVolume;
242 iliev 2217 if (pSignalUnitRack == NULL) {
243 iliev 2205 finalVolume = pEngineChannel->MidiVolume * crossfadeVolume * pEG1->getLevel();
244     } else {
245 iliev 2217 finalVolume = pEngineChannel->MidiVolume * crossfadeVolume * pSignalUnitRack->GetEndpointUnit()->GetVolume();
246 iliev 2205 }
247 iliev 2015
248 persson 2382 finalSynthesisParameters.fFinalVolumeLeft = finalVolume * VolumeLeft * PanLeftSmoother.render();
249     finalSynthesisParameters.fFinalVolumeRight = finalVolume * VolumeRight * PanRightSmoother.render();
250 iliev 2015 }
251     #endif
252     #endif
253    
254 iliev 2217 if (pSignalUnitRack == NULL) {
255 iliev 2205 // setup EG 2 (VCF Cutoff EG)
256     {
257     // get current value of EG2 controller
258     double eg2controllervalue = GetEG2ControllerValue(itNoteOnEvent->Param.Note.Velocity);
259 iliev 2015
260 iliev 2205 // calculate influence of EG2 controller on EG2's parameters
261     EGInfo egInfo = CalculateEG2ControllerInfluence(eg2controllervalue);
262 iliev 2015
263 iliev 2205 TriggerEG2(egInfo, velrelease, velocityAttenuation, GetEngine()->SampleRate, itNoteOnEvent->Param.Note.Velocity);
264     }
265 iliev 2015
266    
267 iliev 2205 // setup EG 3 (VCO EG)
268     {
269     // if portamento mode is on, we dedicate EG3 purely for portamento, otherwise if portamento is off we do as told by the patch
270     bool bPortamento = pEngineChannel->PortamentoMode && pEngineChannel->PortamentoPos >= 0.0f;
271     float eg3depth = (bPortamento)
272 schoenebeck 2879 ? RTMath::CentsToFreqRatio((pEngineChannel->PortamentoPos - (float) MIDIKey()) * 100)
273 iliev 2205 : RTMath::CentsToFreqRatio(RgnInfo.EG3Depth);
274     float eg3time = (bPortamento)
275     ? pEngineChannel->PortamentoTime
276     : RgnInfo.EG3Attack;
277     EG3.trigger(eg3depth, eg3time, GetEngine()->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE);
278     dmsg(5,("PortamentoPos=%f, depth=%f, time=%f\n", pEngineChannel->PortamentoPos, eg3depth, eg3time));
279     }
280 iliev 2015
281    
282 iliev 2205 // setup LFO 1 (VCA LFO)
283     InitLFO1();
284     // setup LFO 2 (VCF Cutoff LFO)
285     InitLFO2();
286     // setup LFO 3 (VCO LFO)
287     InitLFO3();
288     }
289 iliev 2015
290    
291     #if CONFIG_FORCE_FILTER
292     const bool bUseFilter = true;
293     #else // use filter only if instrument file told so
294     const bool bUseFilter = RgnInfo.VCFEnabled;
295     #endif // CONFIG_FORCE_FILTER
296     SYNTHESIS_MODE_SET_FILTER(SynthesisMode, bUseFilter);
297     if (bUseFilter) {
298     #ifdef CONFIG_OVERRIDE_CUTOFF_CTRL
299     VCFCutoffCtrl.controller = CONFIG_OVERRIDE_CUTOFF_CTRL;
300     #else // use the one defined in the instrument file
301     VCFCutoffCtrl.controller = GetVCFCutoffCtrl();
302     #endif // CONFIG_OVERRIDE_CUTOFF_CTRL
303    
304     #ifdef CONFIG_OVERRIDE_RESONANCE_CTRL
305     VCFResonanceCtrl.controller = CONFIG_OVERRIDE_RESONANCE_CTRL;
306     #else // use the one defined in the instrument file
307     VCFResonanceCtrl.controller = GetVCFResonanceCtrl();
308     #endif // CONFIG_OVERRIDE_RESONANCE_CTRL
309    
310     #ifndef CONFIG_OVERRIDE_FILTER_TYPE
311     finalSynthesisParameters.filterLeft.SetType(RgnInfo.VCFType);
312     finalSynthesisParameters.filterRight.SetType(RgnInfo.VCFType);
313     #else // override filter type
314     finalSynthesisParameters.filterLeft.SetType(CONFIG_OVERRIDE_FILTER_TYPE);
315     finalSynthesisParameters.filterRight.SetType(CONFIG_OVERRIDE_FILTER_TYPE);
316     #endif // CONFIG_OVERRIDE_FILTER_TYPE
317    
318     VCFCutoffCtrl.value = pEngineChannel->ControllerTable[VCFCutoffCtrl.controller];
319     VCFResonanceCtrl.value = pEngineChannel->ControllerTable[VCFResonanceCtrl.controller];
320    
321     // calculate cutoff frequency
322     CutoffBase = CalculateCutoffBase(itNoteOnEvent->Param.Note.Velocity);
323    
324     VCFCutoffCtrl.fvalue = CalculateFinalCutoff(CutoffBase);
325    
326     // calculate resonance
327     float resonance = (float) (VCFResonanceCtrl.controller ? VCFResonanceCtrl.value : RgnInfo.VCFResonance);
328     VCFResonanceCtrl.fvalue = resonance;
329     } else {
330     VCFCutoffCtrl.controller = 0;
331     VCFResonanceCtrl.controller = 0;
332     }
333 iliev 2299
334     const bool bEq =
335     pSignalUnitRack != NULL && pSignalUnitRack->HasEq() && pEq->HasSupport();
336 iliev 2015
337 iliev 2299 if (bEq) {
338     pEq->GetInChannelLeft()->Clear();
339     pEq->GetInChannelRight()->Clear();
340     pEq->RenderAudio(GetEngine()->pAudioOutputDevice->MaxSamplesPerCycle());
341     }
342    
343 iliev 2015 return 0; // success
344     }
345 iliev 2216
346     void AbstractVoice::SetSampleStartOffset() {
347     finalSynthesisParameters.dPos = RgnInfo.SampleStartOffset; // offset where we should start playback of sample (0 - 2000 sample points)
348     Pos = RgnInfo.SampleStartOffset;
349     }
350 iliev 2015
351     /**
352     * Synthesizes the current audio fragment for this voice.
353     *
354     * @param Samples - number of sample points to be rendered in this audio
355     * fragment cycle
356     * @param pSrc - pointer to input sample data
357     * @param Skip - number of sample points to skip in output buffer
358     */
359     void AbstractVoice::Synthesize(uint Samples, sample_t* pSrc, uint Skip) {
360 iliev 2297 bool delay = false; // Whether the voice playback should be delayed for this call
361    
362     if (pSignalUnitRack != NULL) {
363     uint delaySteps = pSignalUnitRack->GetEndpointUnit()->DelayTrigger();
364     if (delaySteps > 0) { // delay on the endpoint unit means delay of the voice playback
365     if (delaySteps >= Samples) {
366     pSignalUnitRack->GetEndpointUnit()->DecreaseDelay(Samples);
367     delay = true;
368     } else {
369     pSignalUnitRack->GetEndpointUnit()->DecreaseDelay(delaySteps);
370     Samples -= delaySteps;
371     Skip += delaySteps;
372     }
373     }
374     }
375    
376 iliev 2015 AbstractEngineChannel* pChannel = pEngineChannel;
377 schoenebeck 2879 MidiKeyBase* pMidiKeyInfo = GetMidiKeyInfo(MIDIKey());
378 iliev 2015
379 schoenebeck 2121 const bool bVoiceRequiresDedicatedRouting =
380     pEngineChannel->GetFxSendCount() > 0 &&
381     (pMidiKeyInfo->ReverbSend || pMidiKeyInfo->ChorusSend);
382 iliev 2296
383     const bool bEq =
384 iliev 2298 pSignalUnitRack != NULL && pSignalUnitRack->HasEq() && pEq->HasSupport();
385 schoenebeck 2121
386 iliev 2296 if (bEq) {
387 iliev 2298 pEq->GetInChannelLeft()->Clear();
388     pEq->GetInChannelRight()->Clear();
389     finalSynthesisParameters.pOutLeft = &pEq->GetInChannelLeft()->Buffer()[Skip];
390     finalSynthesisParameters.pOutRight = &pEq->GetInChannelRight()->Buffer()[Skip];
391     pSignalUnitRack->UpdateEqSettings(pEq);
392 iliev 2296 } else if (bVoiceRequiresDedicatedRouting) {
393 schoenebeck 2121 finalSynthesisParameters.pOutLeft = &GetEngine()->pDedicatedVoiceChannelLeft->Buffer()[Skip];
394     finalSynthesisParameters.pOutRight = &GetEngine()->pDedicatedVoiceChannelRight->Buffer()[Skip];
395     } else {
396     finalSynthesisParameters.pOutLeft = &pChannel->pChannelLeft->Buffer()[Skip];
397     finalSynthesisParameters.pOutRight = &pChannel->pChannelRight->Buffer()[Skip];
398     }
399     finalSynthesisParameters.pSrc = pSrc;
400    
401 iliev 2015 RTList<Event>::Iterator itCCEvent = pChannel->pEvents->first();
402     RTList<Event>::Iterator itNoteEvent;
403 schoenebeck 2879 GetFirstEventOnKey(HostKey(), itNoteEvent);
404 iliev 2015
405 persson 2114 RTList<Event>::Iterator itGroupEvent;
406 persson 2352 if (pGroupEvents && !Orphan) itGroupEvent = pGroupEvents->first();
407 persson 2114
408 iliev 2015 if (itTriggerEvent) { // skip events that happened before this voice was triggered
409     while (itCCEvent && itCCEvent->FragmentPos() <= Skip) ++itCCEvent;
410 persson 2114 while (itGroupEvent && itGroupEvent->FragmentPos() <= Skip) ++itGroupEvent;
411    
412 iliev 2015 // we can't simply compare the timestamp here, because note events
413     // might happen on the same time stamp, so we have to deal on the
414     // actual sequence the note events arrived instead (see bug #112)
415     for (; itNoteEvent; ++itNoteEvent) {
416     if (itTriggerEvent == itNoteEvent) {
417     ++itNoteEvent;
418     break;
419     }
420     }
421     }
422    
423 schoenebeck 3054 uint killPos = 0;
424 iliev 2015 if (itKillEvent) {
425     int maxFadeOutPos = Samples - GetEngine()->GetMinFadeOutSamples();
426     if (maxFadeOutPos < 0) {
427     // There's not enough space in buffer to do a fade out
428     // from max volume (this can only happen for audio
429     // drivers that use Samples < MaxSamplesPerCycle).
430     // End the EG1 here, at pos 0, with a shorter max fade
431     // out time.
432 iliev 2217 if (pSignalUnitRack == NULL) {
433 iliev 2205 pEG1->enterFadeOutStage(Samples / CONFIG_DEFAULT_SUBFRAGMENT_SIZE);
434     } else {
435 persson 2327 pSignalUnitRack->EnterFadeOutStage(Samples / CONFIG_DEFAULT_SUBFRAGMENT_SIZE);
436 iliev 2205 }
437 iliev 2015 itKillEvent = Pool<Event>::Iterator();
438     } else {
439     killPos = RTMath::Min(itKillEvent->FragmentPos(), maxFadeOutPos);
440     }
441     }
442    
443     uint i = Skip;
444     while (i < Samples) {
445     int iSubFragmentEnd = RTMath::Min(i + CONFIG_DEFAULT_SUBFRAGMENT_SIZE, Samples);
446    
447     // initialize all final synthesis parameters
448     fFinalCutoff = VCFCutoffCtrl.fvalue;
449     fFinalResonance = VCFResonanceCtrl.fvalue;
450    
451 schoenebeck 2559 // process MIDI control change, aftertouch and pitchbend events for this subfragment
452 iliev 2015 processCCEvents(itCCEvent, iSubFragmentEnd);
453 iliev 2219 uint8_t pan = MIDIPan;
454 persson 2382 if (pSignalUnitRack != NULL) pan = pSignalUnitRack->GetEndpointUnit()->CalculatePan(MIDIPan);
455 iliev 2015
456 schoenebeck 2931 PanLeftSmoother.update(AbstractEngine::PanCurve[128 - pan] * NotePanLeft);
457     PanRightSmoother.update(AbstractEngine::PanCurve[pan] * NotePanRight);
458    
459     finalSynthesisParameters.fFinalPitch = Pitch.PitchBase * Pitch.PitchBend * NotePitch;
460    
461     float fFinalVolume = VolumeSmoother.render() * CrossfadeSmoother.render() * NoteVolumeSmoother.render();
462 iliev 2015 #ifdef CONFIG_PROCESS_MUTED_CHANNELS
463     if (pChannel->GetMute()) fFinalVolume = 0;
464     #endif
465    
466     // process transition events (note on, note off & sustain pedal)
467     processTransitionEvents(itNoteEvent, iSubFragmentEnd);
468 persson 2114 processGroupEvents(itGroupEvent, iSubFragmentEnd);
469 iliev 2297
470 iliev 2217 if (pSignalUnitRack == NULL) {
471 iliev 2205 // if the voice was killed in this subfragment, or if the
472     // filter EG is finished, switch EG1 to fade out stage
473     if ((itKillEvent && killPos <= iSubFragmentEnd) ||
474     (SYNTHESIS_MODE_GET_FILTER(SynthesisMode) &&
475     pEG2->getSegmentType() == EG::segment_end)) {
476     pEG1->enterFadeOutStage();
477     itKillEvent = Pool<Event>::Iterator();
478     }
479 iliev 2015
480 iliev 2205 // process envelope generators
481     switch (pEG1->getSegmentType()) {
482     case EG::segment_lin:
483     fFinalVolume *= pEG1->processLin();
484     break;
485     case EG::segment_exp:
486     fFinalVolume *= pEG1->processExp();
487     break;
488     case EG::segment_end:
489     fFinalVolume *= pEG1->getLevel();
490     break; // noop
491     case EG::segment_pow:
492     fFinalVolume *= pEG1->processPow();
493     break;
494     }
495     switch (pEG2->getSegmentType()) {
496     case EG::segment_lin:
497     fFinalCutoff *= pEG2->processLin();
498     break;
499     case EG::segment_exp:
500     fFinalCutoff *= pEG2->processExp();
501     break;
502     case EG::segment_end:
503     fFinalCutoff *= pEG2->getLevel();
504     break; // noop
505     case EG::segment_pow:
506     fFinalCutoff *= pEG2->processPow();
507     break;
508     }
509     if (EG3.active()) finalSynthesisParameters.fFinalPitch *= EG3.render();
510 iliev 2015
511 iliev 2205 // process low frequency oscillators
512     if (bLFO1Enabled) fFinalVolume *= (1.0f - pLFO1->render());
513 persson 2673 if (bLFO2Enabled) fFinalCutoff *= (1.0f - pLFO2->render());
514 iliev 2205 if (bLFO3Enabled) finalSynthesisParameters.fFinalPitch *= RTMath::CentsToFreqRatio(pLFO3->render());
515     } else {
516 iliev 2322 // if the voice was killed in this subfragment, enter fade out stage
517     if (itKillEvent && killPos <= iSubFragmentEnd) {
518     pSignalUnitRack->EnterFadeOutStage();
519     itKillEvent = Pool<Event>::Iterator();
520     }
521    
522     // if the filter EG is finished, switch EG1 to fade out stage
523     /*if (SYNTHESIS_MODE_GET_FILTER(SynthesisMode) &&
524     pEG2->getSegmentType() == EG::segment_end) {
525 iliev 2205 pEG1->enterFadeOutStage();
526     itKillEvent = Pool<Event>::Iterator();
527     }*/
528     // TODO: ^^^
529 iliev 2015
530 iliev 2217 fFinalVolume *= pSignalUnitRack->GetEndpointUnit()->GetVolume();
531     fFinalCutoff = pSignalUnitRack->GetEndpointUnit()->CalculateFilterCutoff(fFinalCutoff);
532     fFinalResonance = pSignalUnitRack->GetEndpointUnit()->CalculateResonance(fFinalResonance);
533 iliev 2205
534     finalSynthesisParameters.fFinalPitch =
535 iliev 2217 pSignalUnitRack->GetEndpointUnit()->CalculatePitch(finalSynthesisParameters.fFinalPitch);
536 iliev 2205
537     }
538 schoenebeck 2935
539     fFinalCutoff *= NoteCutoff;
540     fFinalResonance *= NoteResonance;
541    
542 iliev 2015 // limit the pitch so we don't read outside the buffer
543     finalSynthesisParameters.fFinalPitch = RTMath::Min(finalSynthesisParameters.fFinalPitch, float(1 << CONFIG_MAX_PITCH));
544    
545     // if filter enabled then update filter coefficients
546     if (SYNTHESIS_MODE_GET_FILTER(SynthesisMode)) {
547     finalSynthesisParameters.filterLeft.SetParameters(fFinalCutoff, fFinalResonance, GetEngine()->SampleRate);
548     finalSynthesisParameters.filterRight.SetParameters(fFinalCutoff, fFinalResonance, GetEngine()->SampleRate);
549     }
550    
551     // do we need resampling?
552     const float __PLUS_ONE_CENT = 1.000577789506554859250142541782224725466f;
553     const float __MINUS_ONE_CENT = 0.9994225441413807496009516495583113737666f;
554     const bool bResamplingRequired = !(finalSynthesisParameters.fFinalPitch <= __PLUS_ONE_CENT &&
555     finalSynthesisParameters.fFinalPitch >= __MINUS_ONE_CENT);
556     SYNTHESIS_MODE_SET_INTERPOLATE(SynthesisMode, bResamplingRequired);
557    
558     // prepare final synthesis parameters structure
559     finalSynthesisParameters.uiToGo = iSubFragmentEnd - i;
560     #ifdef CONFIG_INTERPOLATE_VOLUME
561     finalSynthesisParameters.fFinalVolumeDeltaLeft =
562     (fFinalVolume * VolumeLeft * PanLeftSmoother.render() -
563     finalSynthesisParameters.fFinalVolumeLeft) / finalSynthesisParameters.uiToGo;
564     finalSynthesisParameters.fFinalVolumeDeltaRight =
565     (fFinalVolume * VolumeRight * PanRightSmoother.render() -
566     finalSynthesisParameters.fFinalVolumeRight) / finalSynthesisParameters.uiToGo;
567     #else
568     finalSynthesisParameters.fFinalVolumeLeft =
569     fFinalVolume * VolumeLeft * PanLeftSmoother.render();
570     finalSynthesisParameters.fFinalVolumeRight =
571     fFinalVolume * VolumeRight * PanRightSmoother.render();
572     #endif
573     // render audio for one subfragment
574 iliev 2297 if (!delay) RunSynthesisFunction(SynthesisMode, &finalSynthesisParameters, &loop);
575 iliev 2015
576 iliev 2217 if (pSignalUnitRack == NULL) {
577 iliev 2205 // stop the rendering if volume EG is finished
578     if (pEG1->getSegmentType() == EG::segment_end) break;
579     } else {
580     // stop the rendering if the endpoint unit is not active
581 iliev 2217 if (!pSignalUnitRack->GetEndpointUnit()->Active()) break;
582 iliev 2205 }
583 iliev 2015
584     const double newPos = Pos + (iSubFragmentEnd - i) * finalSynthesisParameters.fFinalPitch;
585    
586 iliev 2217 if (pSignalUnitRack == NULL) {
587 iliev 2205 // increment envelopes' positions
588     if (pEG1->active()) {
589 iliev 2015
590 iliev 2205 // 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
591     if (SmplInfo.HasLoops && Pos <= SmplInfo.LoopStart && SmplInfo.LoopStart < newPos) {
592     pEG1->update(EG::event_hold_end, GetEngine()->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE);
593     }
594    
595     pEG1->increment(1);
596     if (!pEG1->toStageEndLeft()) pEG1->update(EG::event_stage_end, GetEngine()->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE);
597 iliev 2015 }
598 iliev 2205 if (pEG2->active()) {
599     pEG2->increment(1);
600     if (!pEG2->toStageEndLeft()) pEG2->update(EG::event_stage_end, GetEngine()->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE);
601     }
602     EG3.increment(1);
603     if (!EG3.toEndLeft()) EG3.update(); // neutralize envelope coefficient if end reached
604     } else {
605     // 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
606     /*if (SmplInfo.HasLoops && Pos <= SmplInfo.LoopStart && SmplInfo.LoopStart < newPos) {
607     pEG1->update(EG::event_hold_end, GetEngine()->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE);
608     }*/
609     // TODO: ^^^
610    
611 iliev 2297 if (!delay) pSignalUnitRack->Increment();
612 iliev 2015 }
613    
614     Pos = newPos;
615     i = iSubFragmentEnd;
616     }
617 iliev 2297
618     if (delay) return;
619 schoenebeck 2121
620     if (bVoiceRequiresDedicatedRouting) {
621 iliev 2296 if (bEq) {
622 iliev 2298 pEq->RenderAudio(Samples);
623     pEq->GetOutChannelLeft()->CopyTo(GetEngine()->pDedicatedVoiceChannelLeft, Samples);
624     pEq->GetOutChannelRight()->CopyTo(GetEngine()->pDedicatedVoiceChannelRight, Samples);
625 iliev 2296 }
626 schoenebeck 2121 optional<float> effectSendLevels[2] = {
627     pMidiKeyInfo->ReverbSend,
628     pMidiKeyInfo->ChorusSend
629     };
630     GetEngine()->RouteDedicatedVoiceChannels(pEngineChannel, effectSendLevels, Samples);
631 iliev 2296 } else if (bEq) {
632 iliev 2298 pEq->RenderAudio(Samples);
633     pEq->GetOutChannelLeft()->MixTo(pChannel->pChannelLeft, Samples);
634     pEq->GetOutChannelRight()->MixTo(pChannel->pChannelRight, Samples);
635 schoenebeck 2121 }
636 iliev 2015 }
637 persson 2045
638 iliev 2015 /**
639 schoenebeck 2559 * Process given list of MIDI control change, aftertouch and pitch bend
640     * events for the given time.
641 iliev 2015 *
642     * @param itEvent - iterator pointing to the next event to be processed
643     * @param End - youngest time stamp where processing should be stopped
644     */
645     void AbstractVoice::processCCEvents(RTList<Event>::Iterator& itEvent, uint End) {
646     for (; itEvent && itEvent->FragmentPos() <= End; ++itEvent) {
647 schoenebeck 3016 if ((itEvent->Type == Event::type_control_change || itEvent->Type == Event::type_channel_pressure)
648 schoenebeck 3017 && itEvent->Param.CC.Controller) // if (valid) MIDI control change event
649     {
650 iliev 2015 if (itEvent->Param.CC.Controller == VCFCutoffCtrl.controller) {
651     ProcessCutoffEvent(itEvent);
652     }
653     if (itEvent->Param.CC.Controller == VCFResonanceCtrl.controller) {
654     processResonanceEvent(itEvent);
655     }
656 schoenebeck 3017 if (itEvent->Param.CC.Controller == CTRL_TABLE_IDX_AFTERTOUCH ||
657     itEvent->Type == Event::type_channel_pressure)
658     {
659     ProcessChannelPressureEvent(itEvent);
660     }
661 iliev 2217 if (pSignalUnitRack == NULL) {
662 iliev 2205 if (itEvent->Param.CC.Controller == pLFO1->ExtController) {
663     pLFO1->update(itEvent->Param.CC.Value);
664     }
665     if (itEvent->Param.CC.Controller == pLFO2->ExtController) {
666     pLFO2->update(itEvent->Param.CC.Value);
667     }
668     if (itEvent->Param.CC.Controller == pLFO3->ExtController) {
669     pLFO3->update(itEvent->Param.CC.Value);
670     }
671 iliev 2015 }
672     if (itEvent->Param.CC.Controller == 7) { // volume
673     VolumeSmoother.update(AbstractEngine::VolumeCurve[itEvent->Param.CC.Value]);
674     } else if (itEvent->Param.CC.Controller == 10) { // panpot
675 persson 2382 MIDIPan = CalculatePan(itEvent->Param.CC.Value);
676 iliev 2015 }
677     } else if (itEvent->Type == Event::type_pitchbend) { // if pitch bend event
678     processPitchEvent(itEvent);
679 schoenebeck 2559 } else if (itEvent->Type == Event::type_note_pressure) {
680     ProcessPolyphonicKeyPressureEvent(itEvent);
681 iliev 2015 }
682    
683     ProcessCCEvent(itEvent);
684 iliev 2217 if (pSignalUnitRack != NULL) {
685     pSignalUnitRack->ProcessCCEvent(itEvent);
686 iliev 2205 }
687 iliev 2015 }
688     }
689    
690     void AbstractVoice::processPitchEvent(RTList<Event>::Iterator& itEvent) {
691     Pitch.PitchBend = RTMath::CentsToFreqRatio(itEvent->Param.Pitch.Pitch * Pitch.PitchBendRange);
692     }
693    
694     void AbstractVoice::processResonanceEvent(RTList<Event>::Iterator& itEvent) {
695     // convert absolute controller value to differential
696     const int ctrldelta = itEvent->Param.CC.Value - VCFResonanceCtrl.value;
697     VCFResonanceCtrl.value = itEvent->Param.CC.Value;
698     const float resonancedelta = (float) ctrldelta;
699     fFinalResonance += resonancedelta;
700     // needed for initialization of parameter
701     VCFResonanceCtrl.fvalue = itEvent->Param.CC.Value;
702     }
703    
704     /**
705 schoenebeck 2931 * Process given list of MIDI note on, note off, sustain pedal events and
706     * note synthesis parameter events for the given time.
707 iliev 2015 *
708     * @param itEvent - iterator pointing to the next event to be processed
709     * @param End - youngest time stamp where processing should be stopped
710     */
711     void AbstractVoice::processTransitionEvents(RTList<Event>::Iterator& itEvent, uint End) {
712     for (; itEvent && itEvent->FragmentPos() <= End; ++itEvent) {
713 persson 2115 // some voice types ignore note off
714     if (!(Type & (Voice::type_one_shot | Voice::type_release_trigger | Voice::type_controller_triggered))) {
715 schoenebeck 2938 if (itEvent->Type == Event::type_release_key) {
716 persson 2114 EnterReleaseStage();
717 schoenebeck 2938 } else if (itEvent->Type == Event::type_cancel_release_key) {
718 iliev 2217 if (pSignalUnitRack == NULL) {
719 iliev 2205 pEG1->update(EG::event_cancel_release, GetEngine()->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE);
720     pEG2->update(EG::event_cancel_release, GetEngine()->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE);
721     } else {
722 iliev 2217 pSignalUnitRack->CancelRelease();
723 iliev 2205 }
724 persson 2114 }
725 iliev 2015 }
726 schoenebeck 2938 // process stop-note events (caused by built-in instrument script function note_off())
727     if (itEvent->Type == Event::type_release_note && pNote &&
728     pEngineChannel->pEngine->NoteByID( itEvent->Param.Note.ID ) == pNote)
729     {
730     EnterReleaseStage();
731     }
732 schoenebeck 2931 // process synthesis parameter events (caused by built-in realt-time instrument script functions)
733     if (itEvent->Type == Event::type_note_synth_param && pNote &&
734     pEngineChannel->pEngine->NoteByID( itEvent->Param.NoteSynthParam.NoteID ) == pNote)
735     {
736     switch (itEvent->Param.NoteSynthParam.Type) {
737     case Event::synth_param_volume:
738     NoteVolumeSmoother.update(itEvent->Param.NoteSynthParam.AbsValue);
739     break;
740     case Event::synth_param_pitch:
741     NotePitch = itEvent->Param.NoteSynthParam.AbsValue;
742     break;
743     case Event::synth_param_pan:
744     NotePanLeft = AbstractEngine::PanCurveValueNorm(itEvent->Param.NoteSynthParam.AbsValue, 0 /*left*/);
745     NotePanRight = AbstractEngine::PanCurveValueNorm(itEvent->Param.NoteSynthParam.AbsValue, 1 /*right*/);
746     break;
747 schoenebeck 2935 case Event::synth_param_cutoff:
748     NoteCutoff = itEvent->Param.NoteSynthParam.AbsValue;
749     break;
750     case Event::synth_param_resonance:
751     NoteResonance = itEvent->Param.NoteSynthParam.AbsValue;
752     break;
753 schoenebeck 3034
754     case Event::synth_param_attack:
755     case Event::synth_param_decay:
756     case Event::synth_param_release:
757     break; // noop
758 schoenebeck 2931 }
759     }
760 iliev 2015 }
761     }
762    
763 persson 2114 /**
764     * Process given list of events aimed at all voices in a key group.
765     *
766     * @param itEvent - iterator pointing to the next event to be processed
767     * @param End - youngest time stamp where processing should be stopped
768     */
769     void AbstractVoice::processGroupEvents(RTList<Event>::Iterator& itEvent, uint End) {
770     for (; itEvent && itEvent->FragmentPos() <= End; ++itEvent) {
771     ProcessGroupEvent(itEvent);
772     }
773     }
774    
775 iliev 2015 /** @brief Update current portamento position.
776     *
777     * Will be called when portamento mode is enabled to get the final
778     * portamento position of this active voice from where the next voice(s)
779     * might continue to slide on.
780     *
781     * @param itNoteOffEvent - event which causes this voice to die soon
782     */
783     void AbstractVoice::UpdatePortamentoPos(Pool<Event>::Iterator& itNoteOffEvent) {
784 iliev 2217 if (pSignalUnitRack == NULL) {
785 iliev 2205 const float fFinalEG3Level = EG3.level(itNoteOffEvent->FragmentPos());
786 schoenebeck 2879 pEngineChannel->PortamentoPos = (float) MIDIKey() + RTMath::FreqRatioToCents(fFinalEG3Level) * 0.01f;
787 iliev 2205 } else {
788     // TODO:
789     }
790 iliev 2015 }
791    
792     /**
793     * Kill the voice in regular sense. Let the voice render audio until
794     * the kill event actually occured and then fade down the volume level
795     * very quickly and let the voice die finally. Unlike a normal release
796     * of a voice, a kill process cannot be cancalled and is therefore
797     * usually used for voice stealing and key group conflicts.
798     *
799     * @param itKillEvent - event which caused the voice to be killed
800     */
801     void AbstractVoice::Kill(Pool<Event>::Iterator& itKillEvent) {
802     #if CONFIG_DEVMODE
803     if (!itKillEvent) dmsg(1,("AbstractVoice::Kill(): ERROR, !itKillEvent !!!\n"));
804     if (itKillEvent && !itKillEvent.isValid()) dmsg(1,("AbstractVoice::Kill(): ERROR, itKillEvent invalid !!!\n"));
805     #endif // CONFIG_DEVMODE
806    
807     if (itTriggerEvent && itKillEvent->FragmentPos() <= itTriggerEvent->FragmentPos()) return;
808     this->itKillEvent = itKillEvent;
809     }
810    
811     Voice::PitchInfo AbstractVoice::CalculatePitchInfo(int PitchBend) {
812     PitchInfo pitch;
813 schoenebeck 2879 double pitchbasecents = InstrInfo.FineTune + RgnInfo.FineTune + GetEngine()->ScaleTuning[MIDIKey() % 12];
814 iliev 2015
815     // GSt behaviour: maximum transpose up is 40 semitones. If
816     // MIDI key is more than 40 semitones above unity note,
817     // the transpose is not done.
818 schoenebeck 2879 if (!SmplInfo.Unpitched && (MIDIKey() - (int) RgnInfo.UnityNote) < 40) pitchbasecents += (MIDIKey() - (int) RgnInfo.UnityNote) * 100;
819 iliev 2015
820     pitch.PitchBase = RTMath::CentsToFreqRatioUnlimited(pitchbasecents) * (double(SmplInfo.SampleRate) / double(GetEngine()->SampleRate));
821     pitch.PitchBendRange = 1.0 / 8192.0 * 100.0 * InstrInfo.PitchbendRange;
822     pitch.PitchBend = RTMath::CentsToFreqRatio(PitchBend * pitch.PitchBendRange);
823    
824     return pitch;
825     }
826 schoenebeck 2448
827     void AbstractVoice::onScaleTuningChanged() {
828     PitchInfo pitch = this->Pitch;
829 schoenebeck 2879 double pitchbasecents = InstrInfo.FineTune + RgnInfo.FineTune + GetEngine()->ScaleTuning[MIDIKey() % 12];
830 schoenebeck 2448
831     // GSt behaviour: maximum transpose up is 40 semitones. If
832     // MIDI key is more than 40 semitones above unity note,
833     // the transpose is not done.
834 schoenebeck 2879 if (!SmplInfo.Unpitched && (MIDIKey() - (int) RgnInfo.UnityNote) < 40) pitchbasecents += (MIDIKey() - (int) RgnInfo.UnityNote) * 100;
835 schoenebeck 2448
836     pitch.PitchBase = RTMath::CentsToFreqRatioUnlimited(pitchbasecents) * (double(SmplInfo.SampleRate) / double(GetEngine()->SampleRate));
837     this->Pitch = pitch;
838     }
839 iliev 2015
840     double AbstractVoice::CalculateVolume(double velocityAttenuation) {
841     // For 16 bit samples, we downscale by 32768 to convert from
842     // int16 value range to DSP value range (which is
843     // -1.0..1.0). For 24 bit, we downscale from int32.
844     float volume = velocityAttenuation / (SmplInfo.BitDepth == 16 ? 32768.0f : 32768.0f * 65536.0f);
845    
846     volume *= GetSampleAttenuation() * pEngineChannel->GlobalVolume * GLOBAL_VOLUME;
847    
848     // the volume of release triggered samples depends on note length
849 persson 2115 if (Type & Voice::type_release_trigger) {
850 iliev 2015 float noteLength = float(GetEngine()->FrameTime + Delay -
851 schoenebeck 2879 GetNoteOnTime(MIDIKey()) ) / GetEngine()->SampleRate;
852 iliev 2015
853 persson 2061 volume *= GetReleaseTriggerAttenuation(noteLength);
854 iliev 2015 }
855    
856     return volume;
857     }
858 persson 2061
859     float AbstractVoice::GetReleaseTriggerAttenuation(float noteLength) {
860     return 1 - RgnInfo.ReleaseTriggerDecay * noteLength;
861     }
862 persson 2114
863     void AbstractVoice::EnterReleaseStage() {
864 iliev 2217 if (pSignalUnitRack == NULL) {
865 iliev 2205 pEG1->update(EG::event_release, GetEngine()->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE);
866     pEG2->update(EG::event_release, GetEngine()->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE);
867     } else {
868 iliev 2217 pSignalUnitRack->EnterReleaseStage();
869 iliev 2205 }
870 persson 2114 }
871    
872 iliev 2205 bool AbstractVoice::EG1Finished() {
873 iliev 2217 if (pSignalUnitRack == NULL) {
874 iliev 2205 return pEG1->getSegmentType() == EG::segment_end;
875     } else {
876 iliev 2217 return !pSignalUnitRack->GetEndpointUnit()->Active();
877 iliev 2205 }
878     }
879    
880 iliev 2015 } // namespace LinuxSampler

  ViewVC Help
Powered by ViewVC