26 |
|
|
27 |
DiskThread* Voice::pDiskThread = NULL; |
DiskThread* Voice::pDiskThread = NULL; |
28 |
AudioThread* Voice::pEngine = NULL; |
AudioThread* Voice::pEngine = NULL; |
29 |
|
const float Voice::FILTER_CUTOFF_COEFF(CalculateFilterCutoffCoeff()); |
30 |
|
|
31 |
|
float Voice::CalculateFilterCutoffCoeff() { |
32 |
|
return log(FILTER_CUTOFF_MIN / FILTER_CUTOFF_MAX); |
33 |
|
} |
34 |
|
|
35 |
Voice::Voice() { |
Voice::Voice() { |
36 |
Active = false; |
Active = false; |
37 |
pLFO1 = new LFO(ModulationSystem::destination_vca, LFO::manipulation_type_multiply, 0.0f, 1.0f, LFO::propagation_top_down, pEngine->pEventPool); |
pEG1 = new EG_VCA(ModulationSystem::destination_vca); |
38 |
pLFO2 = new LFO(ModulationSystem::destination_vcfc, LFO::manipulation_type_multiply, 0.0f, 1.0f, LFO::propagation_top_down, pEngine->pEventPool); |
pEG2 = new EG_VCA(ModulationSystem::destination_vcfc); |
39 |
pLFO3 = new LFO(ModulationSystem::destination_vco, LFO::manipulation_type_add, -1.0f, 1.0f, LFO::propagation_middle_balanced, pEngine->pEventPool); |
pEG3 = new EG_D(ModulationSystem::destination_vco); |
40 |
|
pLFO1 = new LFO<VCAManipulator>(0.0f, 1.0f, LFO<VCAManipulator>::propagation_top_down, pEngine->pEventPool); |
41 |
|
pLFO2 = new LFO<VCFCManipulator>(0.0f, 1.0f, LFO<VCFCManipulator>::propagation_top_down, pEngine->pEventPool); |
42 |
|
pLFO3 = new LFO<VCOManipulator>(-1200.0f, 1200.0f, LFO<VCOManipulator>::propagation_middle_balanced, pEngine->pEventPool); // +-1 octave (+-1200 cents) max. |
43 |
} |
} |
44 |
|
|
45 |
Voice::~Voice() { |
Voice::~Voice() { |
46 |
|
if (pEG1) delete pEG1; |
47 |
|
if (pEG2) delete pEG2; |
48 |
|
if (pEG3) delete pEG3; |
49 |
if (pLFO1) delete pLFO1; |
if (pLFO1) delete pLFO1; |
50 |
if (pLFO2) delete pLFO2; |
if (pLFO2) delete pLFO2; |
51 |
if (pLFO3) delete pLFO3; |
if (pLFO3) delete pLFO3; |
56 |
* needed. |
* needed. |
57 |
* |
* |
58 |
* @param pNoteOnEvent - event that caused triggering of this voice |
* @param pNoteOnEvent - event that caused triggering of this voice |
59 |
* @param Pitch - MIDI detune factor (-8192 ... +8191) |
* @param PitchBend - MIDI detune factor (-8192 ... +8191) |
60 |
* @param pInstrument - points to the loaded instrument which provides sample wave(s) and articulation data |
* @param pInstrument - points to the loaded instrument which provides sample wave(s) and articulation data |
61 |
* @returns 0 on success, a value < 0 if something failed |
* @returns 0 on success, a value < 0 if something failed |
62 |
*/ |
*/ |
63 |
int Voice::Trigger(ModulationSystem::Event* pNoteOnEvent, int Pitch, gig::Instrument* pInstrument) { |
int Voice::Trigger(ModulationSystem::Event* pNoteOnEvent, int PitchBend, gig::Instrument* pInstrument) { |
64 |
Active = true; |
Active = true; |
65 |
MIDIKey = pNoteOnEvent->Key; |
MIDIKey = pNoteOnEvent->Key; |
66 |
pRegion = pInstrument->GetRegion(MIDIKey); |
pRegion = pInstrument->GetRegion(MIDIKey); |
124 |
} |
} |
125 |
|
|
126 |
|
|
127 |
// Pitch according to keyboard position (if 'PitchTrack' is set) and given detune factor |
// calculate initial pitch value |
128 |
this->Pitch = ((double) Pitch / 8192.0) / 12.0 + ((pDimRgn->PitchTrack) ? pow(2, ((double) (MIDIKey - (int) pDimRgn->UnityNote) + (double) pDimRgn->FineTune / 100.0) / 12.0) |
{ |
129 |
: pow(2, ((double) pDimRgn->FineTune / 100.0) / 12.0)); |
double pitchbasecents = pDimRgn->FineTune * 10; |
130 |
|
if (pDimRgn->PitchTrack) pitchbasecents += (MIDIKey - (int) pDimRgn->UnityNote) * 100; |
131 |
|
this->PitchBase = RTMath::CentsToFreqRatio(pitchbasecents); |
132 |
|
this->PitchBend = RTMath::CentsToFreqRatio(((double) PitchBend / 8192.0) * 200.0); // pitchbend wheel +-2 semitones = 200 cents |
133 |
|
} |
134 |
|
|
135 |
|
|
136 |
Volume = pDimRgn->GetVelocityAttenuation(pNoteOnEvent->Velocity); |
Volume = pDimRgn->GetVelocityAttenuation(pNoteOnEvent->Velocity); |
137 |
|
|
138 |
// get current value of EG1 controller |
|
139 |
double eg1controllervalue; |
// setup EG 1 (VCA EG) |
140 |
switch (pDimRgn->EG1Controller.type) { |
{ |
141 |
case gig::eg1_ctrl_t::type_none: // no controller defined |
// get current value of EG1 controller |
142 |
eg1controllervalue = 0; |
double eg1controllervalue; |
143 |
break; |
switch (pDimRgn->EG1Controller.type) { |
144 |
case gig::eg1_ctrl_t::type_channelaftertouch: |
case gig::eg1_ctrl_t::type_none: // no controller defined |
145 |
eg1controllervalue = 0; // TODO: aftertouch not yet supported |
eg1controllervalue = 0; |
146 |
break; |
break; |
147 |
case gig::eg1_ctrl_t::type_velocity: |
case gig::eg1_ctrl_t::type_channelaftertouch: |
148 |
eg1controllervalue = pNoteOnEvent->Velocity; |
eg1controllervalue = 0; // TODO: aftertouch not yet supported |
149 |
break; |
break; |
150 |
case gig::eg1_ctrl_t::type_controlchange: // MIDI control change controller |
case gig::eg1_ctrl_t::type_velocity: |
151 |
eg1controllervalue = pEngine->ControllerTable[pDimRgn->EG1Controller.controller_number]; |
eg1controllervalue = pNoteOnEvent->Velocity; |
152 |
break; |
break; |
153 |
|
case gig::eg1_ctrl_t::type_controlchange: // MIDI control change controller |
154 |
|
eg1controllervalue = pEngine->ControllerTable[pDimRgn->EG1Controller.controller_number]; |
155 |
|
break; |
156 |
|
} |
157 |
|
if (pDimRgn->EG1ControllerInvert) eg1controllervalue = 127 - eg1controllervalue; |
158 |
|
|
159 |
|
// calculate influence of EG1 controller on EG1's parameters (TODO: needs to be fine tuned) |
160 |
|
double eg1attack = (pDimRgn->EG1ControllerAttackInfluence) ? 0.0001 * (double) (1 << pDimRgn->EG1ControllerAttackInfluence) * eg1controllervalue : 0.0; |
161 |
|
double eg1decay = (pDimRgn->EG1ControllerDecayInfluence) ? 0.0001 * (double) (1 << pDimRgn->EG1ControllerDecayInfluence) * eg1controllervalue : 0.0; |
162 |
|
double eg1release = (pDimRgn->EG1ControllerReleaseInfluence) ? 0.0001 * (double) (1 << pDimRgn->EG1ControllerReleaseInfluence) * eg1controllervalue : 0.0; |
163 |
|
|
164 |
|
pEG1->Trigger(pDimRgn->EG1PreAttack, |
165 |
|
pDimRgn->EG1Attack + eg1attack, |
166 |
|
pDimRgn->EG1Hold, |
167 |
|
pSample->LoopStart, |
168 |
|
pDimRgn->EG1Decay1 + eg1decay, |
169 |
|
pDimRgn->EG1Decay2 + eg1decay, |
170 |
|
pDimRgn->EG1InfiniteSustain, |
171 |
|
pDimRgn->EG1Sustain, |
172 |
|
pDimRgn->EG1Release + eg1release, |
173 |
|
Delay); |
174 |
|
} |
175 |
|
|
176 |
|
|
177 |
|
#if ENABLE_FILTER |
178 |
|
// setup EG 2 (VCF Cutoff EG) |
179 |
|
{ |
180 |
|
// get current value of EG2 controller |
181 |
|
double eg2controllervalue; |
182 |
|
switch (pDimRgn->EG2Controller.type) { |
183 |
|
case gig::eg2_ctrl_t::type_none: // no controller defined |
184 |
|
eg2controllervalue = 0; |
185 |
|
break; |
186 |
|
case gig::eg2_ctrl_t::type_channelaftertouch: |
187 |
|
eg2controllervalue = 0; // TODO: aftertouch not yet supported |
188 |
|
break; |
189 |
|
case gig::eg2_ctrl_t::type_velocity: |
190 |
|
eg2controllervalue = pNoteOnEvent->Velocity; |
191 |
|
break; |
192 |
|
case gig::eg2_ctrl_t::type_controlchange: // MIDI control change controller |
193 |
|
eg2controllervalue = pEngine->ControllerTable[pDimRgn->EG2Controller.controller_number]; |
194 |
|
break; |
195 |
|
} |
196 |
|
if (pDimRgn->EG2ControllerInvert) eg2controllervalue = 127 - eg2controllervalue; |
197 |
|
|
198 |
|
// calculate influence of EG2 controller on EG2's parameters (TODO: needs to be fine tuned) |
199 |
|
double eg2attack = (pDimRgn->EG2ControllerAttackInfluence) ? 0.0001 * (double) (1 << pDimRgn->EG2ControllerAttackInfluence) * eg2controllervalue : 0.0; |
200 |
|
double eg2decay = (pDimRgn->EG2ControllerDecayInfluence) ? 0.0001 * (double) (1 << pDimRgn->EG2ControllerDecayInfluence) * eg2controllervalue : 0.0; |
201 |
|
double eg2release = (pDimRgn->EG2ControllerReleaseInfluence) ? 0.0001 * (double) (1 << pDimRgn->EG2ControllerReleaseInfluence) * eg2controllervalue : 0.0; |
202 |
|
|
203 |
|
pEG2->Trigger(pDimRgn->EG2PreAttack, |
204 |
|
pDimRgn->EG2Attack + eg2attack, |
205 |
|
false, |
206 |
|
pSample->LoopStart, |
207 |
|
pDimRgn->EG2Decay1 + eg2decay, |
208 |
|
pDimRgn->EG2Decay2 + eg2decay, |
209 |
|
pDimRgn->EG2InfiniteSustain, |
210 |
|
pDimRgn->EG2Sustain, |
211 |
|
pDimRgn->EG2Release + eg2release, |
212 |
|
Delay); |
213 |
|
} |
214 |
|
#endif // ENABLE_FILTER |
215 |
|
|
216 |
|
|
217 |
|
// setup EG 3 (VCO EG) |
218 |
|
{ |
219 |
|
double eg3depth = RTMath::CentsToFreqRatio(pDimRgn->EG3Depth); |
220 |
|
pEG3->Trigger(eg3depth, pDimRgn->EG3Attack, Delay); |
221 |
} |
} |
|
if (pDimRgn->EG1ControllerInvert) eg1controllervalue = 127 - eg1controllervalue; |
|
222 |
|
|
|
// calculate influence of EG1 controller on EG1's parameters (TODO: needs to be fine tuned) |
|
|
double eg1attack = (pDimRgn->EG1ControllerAttackInfluence) ? 0.0001 * (double) (1 << pDimRgn->EG1ControllerAttackInfluence) * eg1controllervalue : 0.0; |
|
|
double eg1decay = (pDimRgn->EG1ControllerDecayInfluence) ? 0.0001 * (double) (1 << pDimRgn->EG1ControllerDecayInfluence) * eg1controllervalue : 0.0; |
|
|
double eg1release = (pDimRgn->EG1ControllerReleaseInfluence) ? 0.0001 * (double) (1 << pDimRgn->EG1ControllerReleaseInfluence) * eg1controllervalue : 0.0; |
|
|
|
|
|
EG1.Trigger(pDimRgn->EG1PreAttack, |
|
|
pDimRgn->EG1Attack + eg1attack, |
|
|
pDimRgn->EG1Hold, |
|
|
pSample->LoopStart, |
|
|
pDimRgn->EG1Decay1 + eg1decay, |
|
|
pDimRgn->EG1Decay2 + eg1decay, |
|
|
pDimRgn->EG1InfiniteSustain, |
|
|
pDimRgn->EG1Sustain, |
|
|
pDimRgn->EG1Release + eg1release, |
|
|
Delay); |
|
223 |
|
|
224 |
// setup LFO 1 (VCA LFO) |
// setup LFO 1 (VCA LFO) |
225 |
{ |
{ |
411 |
VCFResonanceCtrl.value = pEngine->ControllerTable[VCFResonanceCtrl.controller]; |
VCFResonanceCtrl.value = pEngine->ControllerTable[VCFResonanceCtrl.controller]; |
412 |
|
|
413 |
// calculate cutoff frequency |
// calculate cutoff frequency |
414 |
float cutoff = (!VCFCutoffCtrl.controller && pDimRgn->VCFVelocityScale) |
float cutoff = (!VCFCutoffCtrl.controller) |
415 |
? (float) pNoteOnEvent->Velocity * pDimRgn->VCFVelocityScale * 0.31f // up to 5kHz |
? exp((float) (127 - pNoteOnEvent->Velocity) * (float) pDimRgn->VCFVelocityScale * 6.2E-5f * FILTER_CUTOFF_COEFF) * FILTER_CUTOFF_MAX |
416 |
: (float) (127 - VCFCutoffCtrl.value) * 39.4f; // up to 5kHz (inverted) |
: exp((float) VCFCutoffCtrl.value * 0.00787402f * FILTER_CUTOFF_COEFF) * FILTER_CUTOFF_MAX; |
417 |
|
|
418 |
// calculate resonance |
// calculate resonance |
419 |
float resonance = (float) VCFResonanceCtrl.value * 0.00787f; // 0.0..1.0 |
float resonance = (float) VCFResonanceCtrl.value * 0.00787f; // 0.0..1.0 |
422 |
} |
} |
423 |
Constrain(resonance, 0.0, 1.0); // correct resonance if outside allowed value range (0.0..1.0) |
Constrain(resonance, 0.0, 1.0); // correct resonance if outside allowed value range (0.0..1.0) |
424 |
|
|
425 |
VCFCutoffCtrl.fvalue = cutoff; |
VCFCutoffCtrl.fvalue = cutoff - FILTER_CUTOFF_MIN; |
426 |
VCFResonanceCtrl.fvalue = resonance; |
VCFResonanceCtrl.fvalue = resonance; |
427 |
|
|
428 |
FilterLeft.SetParameters(cutoff + 20.0f, resonance, ModulationSystem::SampleRate()); // 20Hz min. |
FilterLeft.SetParameters(cutoff, resonance, ModulationSystem::SampleRate()); |
429 |
FilterRight.SetParameters(cutoff + 20.0f, resonance, ModulationSystem::SampleRate()); // 20Hz min. |
FilterRight.SetParameters(cutoff, resonance, ModulationSystem::SampleRate()); |
430 |
|
|
431 |
|
FilterUpdateCounter = -1; |
432 |
} |
} |
433 |
else { |
else { |
434 |
VCFCutoffCtrl.controller = 0; |
VCFCutoffCtrl.controller = 0; |
458 |
|
|
459 |
// Reset the synthesis parameter matrix |
// Reset the synthesis parameter matrix |
460 |
ModulationSystem::ResetDestinationParameter(ModulationSystem::destination_vca, this->Volume); |
ModulationSystem::ResetDestinationParameter(ModulationSystem::destination_vca, this->Volume); |
461 |
ModulationSystem::ResetDestinationParameter(ModulationSystem::destination_vco, this->Pitch); |
ModulationSystem::ResetDestinationParameter(ModulationSystem::destination_vco, this->PitchBase); |
462 |
#if ENABLE_FILTER |
#if ENABLE_FILTER |
463 |
ModulationSystem::ResetDestinationParameter(ModulationSystem::destination_vcfc, VCFCutoffCtrl.fvalue); |
ModulationSystem::ResetDestinationParameter(ModulationSystem::destination_vcfc, VCFCutoffCtrl.fvalue); |
464 |
ModulationSystem::ResetDestinationParameter(ModulationSystem::destination_vcfr, VCFResonanceCtrl.fvalue); |
ModulationSystem::ResetDestinationParameter(ModulationSystem::destination_vcfr, VCFResonanceCtrl.fvalue); |
470 |
|
|
471 |
|
|
472 |
// Let all modulators write their parameter changes to the synthesis parameter matrix for the current audio fragment |
// Let all modulators write their parameter changes to the synthesis parameter matrix for the current audio fragment |
473 |
EG1.Process(Samples, pEngine->pMIDIKeyInfo[MIDIKey].pEvents, pTriggerEvent, this->Pos, this->Pitch); |
pEG1->Process(Samples, pEngine->pMIDIKeyInfo[MIDIKey].pEvents, pTriggerEvent, this->Pos, this->PitchBase * this->PitchBend); |
474 |
|
#if ENABLE_FILTER |
475 |
|
pEG2->Process(Samples, pEngine->pMIDIKeyInfo[MIDIKey].pEvents, pTriggerEvent, this->Pos, this->PitchBase * this->PitchBend); |
476 |
|
#endif // ENABLE_FILTER |
477 |
|
pEG3->Process(Samples); |
478 |
pLFO1->Process(Samples); |
pLFO1->Process(Samples); |
479 |
#if ENABLE_FILTER |
#if ENABLE_FILTER |
480 |
pLFO2->Process(Samples); |
pLFO2->Process(Samples); |
509 |
Kill(); |
Kill(); |
510 |
return; |
return; |
511 |
} |
} |
512 |
DiskStreamRef.pStream->IncrementReadPos(pSample->Channels * (double_to_int(Pos) - MaxRAMPos)); |
DiskStreamRef.pStream->IncrementReadPos(pSample->Channels * (RTMath::DoubleToInt(Pos) - MaxRAMPos)); |
513 |
Pos -= double_to_int(Pos); |
Pos -= RTMath::DoubleToInt(Pos); |
514 |
} |
} |
515 |
|
|
516 |
// add silence sample at the end if we reached the end of the stream (for the interpolator) |
// add silence sample at the end if we reached the end of the stream (for the interpolator) |
521 |
|
|
522 |
sample_t* ptr = DiskStreamRef.pStream->GetReadPtr(); // get the current read_ptr within the ringbuffer where we read the samples from |
sample_t* ptr = DiskStreamRef.pStream->GetReadPtr(); // get the current read_ptr within the ringbuffer where we read the samples from |
523 |
Interpolate(Samples, ptr, Delay); |
Interpolate(Samples, ptr, Delay); |
524 |
DiskStreamRef.pStream->IncrementReadPos(double_to_int(Pos) * pSample->Channels); |
DiskStreamRef.pStream->IncrementReadPos(RTMath::DoubleToInt(Pos) * pSample->Channels); |
525 |
Pos -= double_to_int(Pos); |
Pos -= RTMath::DoubleToInt(Pos); |
526 |
} |
} |
527 |
break; |
break; |
528 |
|
|
544 |
pTriggerEvent = NULL; |
pTriggerEvent = NULL; |
545 |
|
|
546 |
// If release stage finished, let the voice be killed |
// If release stage finished, let the voice be killed |
547 |
if (EG1.GetStage() == EG_VCA::stage_end) this->PlaybackState = playback_state_end; |
if (pEG1->GetStage() == EG_VCA::stage_end) this->PlaybackState = playback_state_end; |
548 |
} |
} |
549 |
|
|
550 |
/** |
/** |
573 |
|
|
574 |
// dispatch control change events |
// dispatch control change events |
575 |
ModulationSystem::Event* pCCEvent = pEngine->pCCEvents->first(); |
ModulationSystem::Event* pCCEvent = pEngine->pCCEvents->first(); |
576 |
|
if (Delay) { // skip events that happened before this voice was triggered |
577 |
|
while (pCCEvent && pCCEvent->FragmentPos() <= Delay) pCCEvent = pEngine->pCCEvents->next(); |
578 |
|
} |
579 |
while (pCCEvent) { |
while (pCCEvent) { |
580 |
if (pCCEvent->Controller) { // if valid MIDI controller |
if (pCCEvent->Controller) { // if valid MIDI controller |
581 |
#if ENABLE_FILTER |
#if ENABLE_FILTER |
602 |
pCCEvent = pEngine->pCCEvents->next(); |
pCCEvent = pEngine->pCCEvents->next(); |
603 |
} |
} |
604 |
|
|
605 |
|
|
606 |
// process pitch events |
// process pitch events |
607 |
RTEList<ModulationSystem::Event>* pVCOEventList = pEngine->pSynthesisEvents[ModulationSystem::destination_vco]; |
{ |
608 |
ModulationSystem::Event* pVCOEvent = pVCOEventList->first(); |
RTEList<ModulationSystem::Event>* pVCOEventList = pEngine->pSynthesisEvents[ModulationSystem::destination_vco]; |
609 |
while (pVCOEvent) { |
ModulationSystem::Event* pVCOEvent = pVCOEventList->first(); |
610 |
ModulationSystem::Event* pNextVCOEvent = pVCOEventList->next(); |
if (Delay) { // skip events that happened before this voice was triggered |
611 |
|
while (pVCOEvent && pVCOEvent->FragmentPos() <= Delay) pVCOEvent = pVCOEventList->next(); |
612 |
|
} |
613 |
|
// apply old pitchbend value until first pitch event occurs |
614 |
|
if (this->PitchBend != 1.0) { |
615 |
|
uint end = (pVCOEvent) ? pVCOEvent->FragmentPos() : Samples; |
616 |
|
for (uint i = Delay; i < end; i++) { |
617 |
|
ModulationSystem::pDestinationParameter[ModulationSystem::destination_vco][i] *= this->PitchBend; |
618 |
|
} |
619 |
|
} |
620 |
|
float pitch; |
621 |
|
while (pVCOEvent) { |
622 |
|
ModulationSystem::Event* pNextVCOEvent = pVCOEventList->next(); |
623 |
|
|
624 |
// calculate the influence length of this event (in sample points) |
// calculate the influence length of this event (in sample points) |
625 |
uint end = (pNextVCOEvent) ? pNextVCOEvent->FragmentPos() : Samples; |
uint end = (pNextVCOEvent) ? pNextVCOEvent->FragmentPos() : Samples; |
626 |
|
|
627 |
this->Pitch += ((double) pVCOEvent->Pitch / 8192.0) / 12.0; // +- one semitone |
pitch = RTMath::CentsToFreqRatio(((double) pVCOEvent->Pitch / 8192.0) * 200.0); // +-two semitones = +-200 cents |
628 |
|
|
629 |
// apply pitch value to the pitch parameter sequence |
// apply pitch value to the pitch parameter sequence |
630 |
for (uint i = pVCOEvent->FragmentPos(); i < end; i++) { |
for (uint i = pVCOEvent->FragmentPos(); i < end; i++) { |
631 |
ModulationSystem::pDestinationParameter[ModulationSystem::destination_vco][i] = this->Pitch; |
ModulationSystem::pDestinationParameter[ModulationSystem::destination_vco][i] *= pitch; |
632 |
} |
} |
633 |
|
|
634 |
pVCOEvent = pNextVCOEvent; |
pVCOEvent = pNextVCOEvent; |
635 |
|
} |
636 |
|
if (pVCOEventList->last()) this->PitchBend = pitch; |
637 |
} |
} |
638 |
|
|
639 |
|
|
640 |
#if ENABLE_FILTER |
#if ENABLE_FILTER |
641 |
// process filter cutoff events |
// process filter cutoff events |
642 |
RTEList<ModulationSystem::Event>* pCutoffEventList = pEngine->pSynthesisEvents[ModulationSystem::destination_vcfc]; |
{ |
643 |
ModulationSystem::Event* pCutoffEvent = pCutoffEventList->first(); |
RTEList<ModulationSystem::Event>* pCutoffEventList = pEngine->pSynthesisEvents[ModulationSystem::destination_vcfc]; |
644 |
while (pCutoffEvent) { |
ModulationSystem::Event* pCutoffEvent = pCutoffEventList->first(); |
645 |
ModulationSystem::Event* pNextCutoffEvent = pCutoffEventList->next(); |
if (Delay) { // skip events that happened before this voice was triggered |
646 |
|
while (pCutoffEvent && pCutoffEvent->FragmentPos() <= Delay) pCutoffEvent = pCutoffEventList->next(); |
647 |
|
} |
648 |
|
float cutoff; |
649 |
|
while (pCutoffEvent) { |
650 |
|
ModulationSystem::Event* pNextCutoffEvent = pCutoffEventList->next(); |
651 |
|
|
652 |
// calculate the influence length of this event (in sample points) |
// calculate the influence length of this event (in sample points) |
653 |
uint end = (pNextCutoffEvent) ? pNextCutoffEvent->FragmentPos() : Samples; |
uint end = (pNextCutoffEvent) ? pNextCutoffEvent->FragmentPos() : Samples; |
654 |
|
|
655 |
// convert absolute controller value to differential |
cutoff = exp((float) pCutoffEvent->Value * 0.00787402f * FILTER_CUTOFF_COEFF) * FILTER_CUTOFF_MAX - FILTER_CUTOFF_MIN; |
|
int ctrldelta = pCutoffEvent->Value - VCFCutoffCtrl.value; |
|
|
VCFCutoffCtrl.value = pCutoffEvent->Value; |
|
656 |
|
|
657 |
float cutoffdelta = (float) ctrldelta * -39.4f; // (20Hz)..5kHz (inverted) |
// apply cutoff frequency to the cutoff parameter sequence |
658 |
|
for (uint i = pCutoffEvent->FragmentPos(); i < end; i++) { |
659 |
|
ModulationSystem::pDestinationParameter[ModulationSystem::destination_vcfc][i] = cutoff; |
660 |
|
} |
661 |
|
|
662 |
// apply cutoff frequency to the cutoff parameter sequence |
pCutoffEvent = pNextCutoffEvent; |
|
for (uint i = pCutoffEvent->FragmentPos(); i < end; i++) { |
|
|
ModulationSystem::pDestinationParameter[ModulationSystem::destination_vcfc][i] += cutoffdelta; |
|
663 |
} |
} |
664 |
|
if (pCutoffEventList->last()) VCFCutoffCtrl.fvalue = cutoff; // needed for initialization of parameter matrix next time |
|
pCutoffEvent = pNextCutoffEvent; |
|
665 |
} |
} |
|
if (pCutoffEventList->last()) VCFCutoffCtrl.fvalue = (float) (127 - pCutoffEventList->last()->Value) * 39.4f; // needed for initialization of parameter matrix next time |
|
666 |
|
|
667 |
// process filter resonance events |
// process filter resonance events |
668 |
RTEList<ModulationSystem::Event>* pResonanceEventList = pEngine->pSynthesisEvents[ModulationSystem::destination_vcfr]; |
{ |
669 |
ModulationSystem::Event* pResonanceEvent = pResonanceEventList->first(); |
RTEList<ModulationSystem::Event>* pResonanceEventList = pEngine->pSynthesisEvents[ModulationSystem::destination_vcfr]; |
670 |
while (pResonanceEvent) { |
ModulationSystem::Event* pResonanceEvent = pResonanceEventList->first(); |
671 |
ModulationSystem::Event* pNextResonanceEvent = pResonanceEventList->next(); |
if (Delay) { // skip events that happened before this voice was triggered |
672 |
|
while (pResonanceEvent && pResonanceEvent->FragmentPos() <= Delay) pResonanceEvent = pResonanceEventList->next(); |
673 |
|
} |
674 |
|
while (pResonanceEvent) { |
675 |
|
ModulationSystem::Event* pNextResonanceEvent = pResonanceEventList->next(); |
676 |
|
|
677 |
// calculate the influence length of this event (in sample points) |
// calculate the influence length of this event (in sample points) |
678 |
uint end = (pNextResonanceEvent) ? pNextResonanceEvent->FragmentPos() : Samples; |
uint end = (pNextResonanceEvent) ? pNextResonanceEvent->FragmentPos() : Samples; |
679 |
|
|
680 |
// convert absolute controller value to differential |
// convert absolute controller value to differential |
681 |
int ctrldelta = pResonanceEvent->Value - VCFResonanceCtrl.value; |
int ctrldelta = pResonanceEvent->Value - VCFResonanceCtrl.value; |
682 |
VCFResonanceCtrl.value = pResonanceEvent->Value; |
VCFResonanceCtrl.value = pResonanceEvent->Value; |
683 |
|
|
684 |
float resonancedelta = (float) ctrldelta * 0.00787f; // 0.0..1.0 |
float resonancedelta = (float) ctrldelta * 0.00787f; // 0.0..1.0 |
685 |
|
|
686 |
// apply cutoff frequency to the cutoff parameter sequence |
// apply cutoff frequency to the cutoff parameter sequence |
687 |
for (uint i = pResonanceEvent->FragmentPos(); i < end; i++) { |
for (uint i = pResonanceEvent->FragmentPos(); i < end; i++) { |
688 |
ModulationSystem::pDestinationParameter[ModulationSystem::destination_vcfr][i] += resonancedelta; |
ModulationSystem::pDestinationParameter[ModulationSystem::destination_vcfr][i] += resonancedelta; |
689 |
} |
} |
690 |
|
|
691 |
pResonanceEvent = pNextResonanceEvent; |
pResonanceEvent = pNextResonanceEvent; |
692 |
|
} |
693 |
|
if (pResonanceEventList->last()) VCFResonanceCtrl.fvalue = pResonanceEventList->last()->Value * 0.00787f; // needed for initialization of parameter matrix next time |
694 |
} |
} |
|
if (pResonanceEventList->last()) VCFResonanceCtrl.fvalue = pResonanceEventList->last()->Value * 0.00787f; // needed for initialization of parameter matrix next time |
|
695 |
#endif // ENABLE_FILTER |
#endif // ENABLE_FILTER |
696 |
} |
} |
697 |
|
|
715 |
ModulationSystem::pDestinationParameter[ModulationSystem::destination_vcfc][i], |
ModulationSystem::pDestinationParameter[ModulationSystem::destination_vcfc][i], |
716 |
ModulationSystem::pDestinationParameter[ModulationSystem::destination_vcfr][i]); |
ModulationSystem::pDestinationParameter[ModulationSystem::destination_vcfr][i]); |
717 |
} |
} |
|
|
|
|
#if ENABLE_FILTER |
|
|
// to save the last filter setting for the next render cycle (only needed when we update the filter not-sample-accurate) |
|
|
ForceUpdateFilter_Stereo(ModulationSystem::pDestinationParameter[ModulationSystem::destination_vcfc][Samples - 1], |
|
|
ModulationSystem::pDestinationParameter[ModulationSystem::destination_vcfr][Samples - 1]); |
|
|
#endif // ENABLE_FILTER |
|
718 |
} |
} |
719 |
else { // Mono Sample |
else { // Mono Sample |
720 |
while (i < Samples) { |
while (i < Samples) { |
724 |
ModulationSystem::pDestinationParameter[ModulationSystem::destination_vcfc][i], |
ModulationSystem::pDestinationParameter[ModulationSystem::destination_vcfc][i], |
725 |
ModulationSystem::pDestinationParameter[ModulationSystem::destination_vcfr][i]); |
ModulationSystem::pDestinationParameter[ModulationSystem::destination_vcfr][i]); |
726 |
} |
} |
|
|
|
|
#if ENABLE_FILTER |
|
|
// to save the last filter setting for the next render cycle (only needed when we update the filter not-sample-accurate) |
|
|
ForceUpdateFilter_Mono(ModulationSystem::pDestinationParameter[ModulationSystem::destination_vcfc][Samples - 1], |
|
|
ModulationSystem::pDestinationParameter[ModulationSystem::destination_vcfr][Samples - 1]); |
|
|
#endif // ENABLE_FILTER |
|
727 |
} |
} |
728 |
} |
} |
729 |
|
|
774 |
} |
} |
775 |
} |
} |
776 |
} |
} |
|
#if ENABLE_FILTER |
|
|
// to save the last filter setting for the next render cycle (only needed when we update the filter not-sample-accurate) |
|
|
ForceUpdateFilter_Stereo(ModulationSystem::pDestinationParameter[ModulationSystem::destination_vcfc][Samples - 1], |
|
|
ModulationSystem::pDestinationParameter[ModulationSystem::destination_vcfr][Samples - 1]); |
|
|
#endif // ENABLE_FILTER |
|
777 |
} |
} |
778 |
else { // Mono Sample |
else { // Mono Sample |
779 |
if (pSample->LoopPlayCount) { |
if (pSample->LoopPlayCount) { |
810 |
} |
} |
811 |
} |
} |
812 |
} |
} |
|
#if ENABLE_FILTER |
|
|
// to save the last filter setting for the next render cycle (only needed when we update the filter not-sample-accurate) |
|
|
ForceUpdateFilter_Mono(ModulationSystem::pDestinationParameter[ModulationSystem::destination_vcfc][Samples - 1], |
|
|
ModulationSystem::pDestinationParameter[ModulationSystem::destination_vcfr][Samples - 1]); |
|
|
#endif // ENABLE_FILTER |
|
813 |
} |
} |
814 |
} |
} |
815 |
|
|