--- linuxsampler/trunk/src/engines/gig/Voice.cpp 2005/05/22 20:43:32 563 +++ linuxsampler/trunk/src/engines/gig/Voice.cpp 2005/10/30 08:35:13 796 @@ -21,10 +21,9 @@ * MA 02111-1307 USA * ***************************************************************************/ -#include "EGADSR.h" -#include "Manipulator.h" #include "../../common/Features.h" #include "Synthesizer.h" +#include "Profiler.h" #include "Voice.h" @@ -32,83 +31,39 @@ const float Voice::FILTER_CUTOFF_COEFF(CalculateFilterCutoffCoeff()); - const int Voice::FILTER_UPDATE_MASK(CalculateFilterUpdateMask()); - float Voice::CalculateFilterCutoffCoeff() { - return log(CONFIG_FILTER_CUTOFF_MIN / CONFIG_FILTER_CUTOFF_MAX); - } - - int Voice::CalculateFilterUpdateMask() { - if (CONFIG_FILTER_UPDATE_STEPS <= 0) return 0; - int power_of_two; - for (power_of_two = 0; 1<pEngine = pEngine; - - // delete old objects - if (pEG1) delete pEG1; - if (pEG2) delete pEG2; - if (pEG3) delete pEG3; - if (pVCAManipulator) delete pVCAManipulator; - if (pVCFCManipulator) delete pVCFCManipulator; - if (pVCOManipulator) delete pVCOManipulator; - if (pLFO1) delete pLFO1; - if (pLFO2) delete pLFO2; - if (pLFO3) delete pLFO3; - - // create new ones - pEG1 = new EGADSR(pEngine, Event::destination_vca); - pEG2 = new EGADSR(pEngine, Event::destination_vcfc); - pEG3 = new EGDecay(pEngine, Event::destination_vco); - pVCAManipulator = new VCAManipulator(pEngine); - pVCFCManipulator = new VCFCManipulator(pEngine); - pVCOManipulator = new VCOManipulator(pEngine); - pLFO1 = new LFO(0.0f, 1.0f, LFO::propagation_top_down, pVCAManipulator, pEngine->pEventPool); - pLFO2 = new LFO(0.0f, 1.0f, LFO::propagation_top_down, pVCFCManipulator, pEngine->pEventPool); - pLFO3 = new LFO(-1200.0f, 1200.0f, LFO::propagation_middle_balanced, pVCOManipulator, pEngine->pEventPool); // +-1 octave (+-1200 cents) max. - + this->pEngine = pEngine; this->pDiskThread = pEngine->pDiskThread; dmsg(6,("Voice::SetEngine()\n")); } @@ -117,157 +72,50 @@ * Initializes and triggers the voice, a disk stream will be launched if * needed. * - * @param pEngineChannel - engine channel on which this voice was ordered - * @param itNoteOnEvent - event that caused triggering of this voice - * @param PitchBend - MIDI detune factor (-8192 ... +8191) - * @param pInstrument - points to the loaded instrument which provides sample wave(s) and articulation data - * @param iLayer - layer number this voice refers to (only if this is a layered sound of course) - * @param ReleaseTriggerVoice - if this new voice is a release trigger voice (optional, default = false) - * @param VoiceStealingAllowed - wether the voice is allowed to steal voices for further subvoices + * @param pEngineChannel - engine channel on which this voice was ordered + * @param itNoteOnEvent - event that caused triggering of this voice + * @param PitchBend - MIDI detune factor (-8192 ... +8191) + * @param pDimRgn - points to the dimension region which provides sample wave(s) and articulation data + * @param VoiceType - type of this voice + * @param iKeyGroup - a value > 0 defines a key group in which this voice is member of * @returns 0 on success, a value < 0 if the voice wasn't triggered * (either due to an error or e.g. because no region is * defined for the given key) */ - int Voice::Trigger(EngineChannel* pEngineChannel, Pool::Iterator& itNoteOnEvent, int PitchBend, ::gig::Instrument* pInstrument, int iLayer, bool ReleaseTriggerVoice, bool VoiceStealingAllowed) { + int Voice::Trigger(EngineChannel* pEngineChannel, Pool::Iterator& itNoteOnEvent, int PitchBend, ::gig::DimensionRegion* pDimRgn, type_t VoiceType, int iKeyGroup) { this->pEngineChannel = pEngineChannel; - if (!pInstrument) { - dmsg(1,("voice::trigger: !pInstrument\n")); - exit(EXIT_FAILURE); - } + this->pDimRgn = pDimRgn; + #if CONFIG_DEVMODE if (itNoteOnEvent->FragmentPos() > pEngine->MaxSamplesPerCycle) { // just a sanity check for debugging dmsg(1,("Voice::Trigger(): ERROR, TriggerDelay > Totalsamples\n")); } #endif // CONFIG_DEVMODE - Type = type_normal; + Type = VoiceType; MIDIKey = itNoteOnEvent->Param.Note.Key; - pRegion = pInstrument->GetRegion(MIDIKey); PlaybackState = playback_state_init; // mark voice as triggered, but no audio rendered yet Delay = itNoteOnEvent->FragmentPos(); itTriggerEvent = itNoteOnEvent; itKillEvent = Pool::Iterator(); + KeyGroup = iKeyGroup; + pSample = pDimRgn->pSample; // sample won't change until the voice is finished - if (!pRegion) { - dmsg(4, ("gig::Voice: No Region defined for MIDI key %d\n", MIDIKey)); - return -1; - } + // calculate volume + const double velocityAttenuation = pDimRgn->GetVelocityAttenuation(itNoteOnEvent->Param.Note.Velocity); - // only mark the first voice of a layered voice (group) to be in a - // key group, so the layered voices won't kill each other - KeyGroup = (iLayer == 0 && !ReleaseTriggerVoice) ? pRegion->KeyGroup : 0; + Volume = velocityAttenuation / 32768.0f; // we downscale by 32768 to convert from int16 value range to DSP value range (which is -1.0..1.0) - // get current dimension values to select the right dimension region - //FIXME: controller values for selecting the dimension region here are currently not sample accurate - uint DimValues[8] = { 0 }; - for (int i = pRegion->Dimensions - 1; i >= 0; i--) { - switch (pRegion->pDimensionDefinitions[i].dimension) { - case ::gig::dimension_samplechannel: - DimValues[i] = 0; //TODO: we currently ignore this dimension - break; - case ::gig::dimension_layer: - DimValues[i] = iLayer; - break; - case ::gig::dimension_velocity: - DimValues[i] = itNoteOnEvent->Param.Note.Velocity; - break; - case ::gig::dimension_channelaftertouch: - DimValues[i] = 0; //TODO: we currently ignore this dimension - break; - case ::gig::dimension_releasetrigger: - Type = (ReleaseTriggerVoice) ? type_release_trigger : (!iLayer) ? type_release_trigger_required : type_normal; - DimValues[i] = (uint) ReleaseTriggerVoice; - break; - case ::gig::dimension_keyboard: - DimValues[i] = (uint) pEngineChannel->CurrentKeyDimension; - break; - case ::gig::dimension_roundrobin: - DimValues[i] = (uint) pEngineChannel->pMIDIKeyInfo[MIDIKey].RoundRobinIndex; // incremented for each note on - break; - case ::gig::dimension_random: - pEngine->RandomSeed = pEngine->RandomSeed * 1103515245 + 12345; // classic pseudo random number generator - DimValues[i] = (uint) pEngine->RandomSeed >> (32 - pRegion->pDimensionDefinitions[i].bits); // highest bits are most random - break; - case ::gig::dimension_modwheel: - DimValues[i] = pEngineChannel->ControllerTable[1]; - break; - case ::gig::dimension_breath: - DimValues[i] = pEngineChannel->ControllerTable[2]; - break; - case ::gig::dimension_foot: - DimValues[i] = pEngineChannel->ControllerTable[4]; - break; - case ::gig::dimension_portamentotime: - DimValues[i] = pEngineChannel->ControllerTable[5]; - break; - case ::gig::dimension_effect1: - DimValues[i] = pEngineChannel->ControllerTable[12]; - break; - case ::gig::dimension_effect2: - DimValues[i] = pEngineChannel->ControllerTable[13]; - break; - case ::gig::dimension_genpurpose1: - DimValues[i] = pEngineChannel->ControllerTable[16]; - break; - case ::gig::dimension_genpurpose2: - DimValues[i] = pEngineChannel->ControllerTable[17]; - break; - case ::gig::dimension_genpurpose3: - DimValues[i] = pEngineChannel->ControllerTable[18]; - break; - case ::gig::dimension_genpurpose4: - DimValues[i] = pEngineChannel->ControllerTable[19]; - break; - case ::gig::dimension_sustainpedal: - DimValues[i] = pEngineChannel->ControllerTable[64]; - break; - case ::gig::dimension_portamento: - DimValues[i] = pEngineChannel->ControllerTable[65]; - break; - case ::gig::dimension_sostenutopedal: - DimValues[i] = pEngineChannel->ControllerTable[66]; - break; - case ::gig::dimension_softpedal: - DimValues[i] = pEngineChannel->ControllerTable[67]; - break; - case ::gig::dimension_genpurpose5: - DimValues[i] = pEngineChannel->ControllerTable[80]; - break; - case ::gig::dimension_genpurpose6: - DimValues[i] = pEngineChannel->ControllerTable[81]; - break; - case ::gig::dimension_genpurpose7: - DimValues[i] = pEngineChannel->ControllerTable[82]; - break; - case ::gig::dimension_genpurpose8: - DimValues[i] = pEngineChannel->ControllerTable[83]; - break; - case ::gig::dimension_effect1depth: - DimValues[i] = pEngineChannel->ControllerTable[91]; - break; - case ::gig::dimension_effect2depth: - DimValues[i] = pEngineChannel->ControllerTable[92]; - break; - case ::gig::dimension_effect3depth: - DimValues[i] = pEngineChannel->ControllerTable[93]; - break; - case ::gig::dimension_effect4depth: - DimValues[i] = pEngineChannel->ControllerTable[94]; - break; - case ::gig::dimension_effect5depth: - DimValues[i] = pEngineChannel->ControllerTable[95]; - break; - case ::gig::dimension_none: - std::cerr << "gig::Voice::Trigger() Error: dimension=none\n" << std::flush; - break; - default: - std::cerr << "gig::Voice::Trigger() Error: Unknown dimension\n" << std::flush; - } - } - pDimRgn = pRegion->GetDimensionRegionByValue(DimValues); + Volume *= pDimRgn->SampleAttenuation; - pSample = pDimRgn->pSample; // sample won't change until the voice is finished - if (!pSample || !pSample->SamplesTotal) return -1; // no need to continue if sample is silent + // the volume of release triggered samples depends on note length + if (Type == type_release_trigger) { + float noteLength = float(pEngine->FrameTime + Delay - + pEngineChannel->pMIDIKeyInfo[MIDIKey].NoteOnTime) / pEngine->SampleRate; + float attenuation = 1 - 0.01053 * (256 >> pDimRgn->ReleaseTriggerDecay) * noteLength; + if (attenuation <= 0) return -1; + Volume *= attenuation; + } // select channel mode (mono or stereo) SYNTHESIS_MODE_SET_CHANNELS(SynthesisMode, pSample->Channels == 2); @@ -291,7 +139,8 @@ PanLeft = 1.0f - float(RTMath::Max(pDimRgn->Pan, 0)) / 63.0f; PanRight = 1.0f - float(RTMath::Min(pDimRgn->Pan, 0)) / -64.0f; - Pos = pDimRgn->SampleStartOffset; // offset where we should start playback of sample (0 - 2000 sample points) + finalSynthesisParameters.dPos = pDimRgn->SampleStartOffset; // offset where we should start playback of sample (0 - 2000 sample points) + Pos = pDimRgn->SampleStartOffset; // Check if the sample needs disk streaming or is too short for that long cachedsamples = pSample->GetCache().Size / pSample->FrameSize; @@ -302,8 +151,12 @@ // check if there's a loop defined which completely fits into the cached (RAM) part of the sample if (pSample->Loops && pSample->LoopEnd <= MaxRAMPos) { - RAMLoop = true; - LoopCyclesLeft = pSample->LoopPlayCount; + RAMLoop = true; + loop.uiTotalCycles = pSample->LoopPlayCount; + loop.uiCyclesLeft = pSample->LoopPlayCount; + loop.uiStart = pSample->LoopStart; + loop.uiEnd = pSample->LoopEnd; + loop.uiSize = pSample->LoopSize; } else RAMLoop = false; @@ -317,8 +170,8 @@ else { // RAM only voice MaxRAMPos = cachedsamples; if (pSample->Loops) { - RAMLoop = true; - LoopCyclesLeft = pSample->LoopPlayCount; + RAMLoop = true; + loop.uiCyclesLeft = pSample->LoopPlayCount; } else RAMLoop = false; dmsg(4,("RAM only voice launched (Looping: %s)\n", (RAMLoop) ? "yes" : "no")); @@ -329,13 +182,12 @@ { double pitchbasecents = pDimRgn->FineTune + (int) pEngine->ScaleTuning[MIDIKey % 12]; if (pDimRgn->PitchTrack) pitchbasecents += (MIDIKey - (int) pDimRgn->UnityNote) * 100; - this->PitchBase = RTMath::CentsToFreqRatio(pitchbasecents) * (double(pSample->SamplesPerSecond) / double(pEngine->pAudioOutputDevice->SampleRate())); + this->PitchBase = RTMath::CentsToFreqRatio(pitchbasecents) * (double(pSample->SamplesPerSecond) / double(pEngine->SampleRate)); this->PitchBend = RTMath::CentsToFreqRatio(((double) PitchBend / 8192.0) * 200.0); // pitchbend wheel +-2 semitones = 200 cents } - Volume = pDimRgn->GetVelocityAttenuation(itNoteOnEvent->Param.Note.Velocity) / 32768.0f; // we downscale by 32768 to convert from int16 value range to DSP value range (which is -1.0..1.0) - - Volume *= pDimRgn->SampleAttenuation; + // the length of the decay and release curves are dependent on the velocity + const double velrelease = 1 / pDimRgn->GetVelocityRelease(itNoteOnEvent->Param.Note.Velocity); // setup EG 1 (VCA EG) { @@ -357,24 +209,24 @@ } if (pDimRgn->EG1ControllerInvert) eg1controllervalue = 127 - eg1controllervalue; - // 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; - - pEG1->Trigger(pDimRgn->EG1PreAttack, - pDimRgn->EG1Attack + eg1attack, - pDimRgn->EG1Hold, - pSample->LoopStart, - pDimRgn->EG1Decay1 + eg1decay, - pDimRgn->EG1Decay2 + eg1decay, - pDimRgn->EG1InfiniteSustain, - pDimRgn->EG1Sustain, - pDimRgn->EG1Release + eg1release, - // the SSE synthesis implementation requires - // the vca start to be 16 byte aligned - SYNTHESIS_MODE_GET_IMPLEMENTATION(SynthesisMode) ? - Delay & 0xfffffffc : Delay); + // calculate influence of EG1 controller on EG1's parameters + // (eg1attack is different from the others) + double eg1attack = (pDimRgn->EG1ControllerAttackInfluence) ? + 1 + 0.031 * (double) (pDimRgn->EG1ControllerAttackInfluence == 1 ? + 1 : 1 << pDimRgn->EG1ControllerAttackInfluence) * eg1controllervalue : 1.0; + double eg1decay = (pDimRgn->EG1ControllerDecayInfluence) ? 1 + 0.00775 * (double) (1 << pDimRgn->EG1ControllerDecayInfluence) * eg1controllervalue : 1.0; + double eg1release = (pDimRgn->EG1ControllerReleaseInfluence) ? 1 + 0.00775 * (double) (1 << pDimRgn->EG1ControllerReleaseInfluence) * eg1controllervalue : 1.0; + + EG1.trigger(pDimRgn->EG1PreAttack, + pDimRgn->EG1Attack * eg1attack, + pDimRgn->EG1Hold, + pDimRgn->EG1Decay1 * eg1decay * velrelease, + pDimRgn->EG1Decay2 * eg1decay * velrelease, + pDimRgn->EG1InfiniteSustain, + pDimRgn->EG1Sustain, + pDimRgn->EG1Release * eg1release * velrelease, + velocityAttenuation, + pEngine->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE); } @@ -398,28 +250,28 @@ } if (pDimRgn->EG2ControllerInvert) eg2controllervalue = 127 - eg2controllervalue; - // calculate influence of EG2 controller on EG2's parameters (TODO: needs to be fine tuned) - double eg2attack = (pDimRgn->EG2ControllerAttackInfluence) ? 0.0001 * (double) (1 << pDimRgn->EG2ControllerAttackInfluence) * eg2controllervalue : 0.0; - double eg2decay = (pDimRgn->EG2ControllerDecayInfluence) ? 0.0001 * (double) (1 << pDimRgn->EG2ControllerDecayInfluence) * eg2controllervalue : 0.0; - double eg2release = (pDimRgn->EG2ControllerReleaseInfluence) ? 0.0001 * (double) (1 << pDimRgn->EG2ControllerReleaseInfluence) * eg2controllervalue : 0.0; - - pEG2->Trigger(pDimRgn->EG2PreAttack, - pDimRgn->EG2Attack + eg2attack, - false, - pSample->LoopStart, - pDimRgn->EG2Decay1 + eg2decay, - pDimRgn->EG2Decay2 + eg2decay, - pDimRgn->EG2InfiniteSustain, - pDimRgn->EG2Sustain, - pDimRgn->EG2Release + eg2release, - Delay); + // calculate influence of EG2 controller on EG2's parameters + double eg2attack = (pDimRgn->EG2ControllerAttackInfluence) ? 1 + 0.00775 * (double) (1 << pDimRgn->EG2ControllerAttackInfluence) * eg2controllervalue : 1.0; + double eg2decay = (pDimRgn->EG2ControllerDecayInfluence) ? 1 + 0.00775 * (double) (1 << pDimRgn->EG2ControllerDecayInfluence) * eg2controllervalue : 1.0; + double eg2release = (pDimRgn->EG2ControllerReleaseInfluence) ? 1 + 0.00775 * (double) (1 << pDimRgn->EG2ControllerReleaseInfluence) * eg2controllervalue : 1.0; + + EG2.trigger(pDimRgn->EG2PreAttack, + pDimRgn->EG2Attack * eg2attack, + false, + pDimRgn->EG2Decay1 * eg2decay * velrelease, + pDimRgn->EG2Decay2 * eg2decay * velrelease, + pDimRgn->EG2InfiniteSustain, + pDimRgn->EG2Sustain, + pDimRgn->EG2Release * eg2release * velrelease, + velocityAttenuation, + pEngine->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE); } // setup EG 3 (VCO EG) { double eg3depth = RTMath::CentsToFreqRatio(pDimRgn->EG3Depth); - pEG3->Trigger(eg3depth, pDimRgn->EG3Attack, Delay); + EG3.trigger(eg3depth, pDimRgn->EG3Attack, pEngine->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE); } @@ -430,34 +282,39 @@ case ::gig::lfo1_ctrl_internal: lfo1_internal_depth = pDimRgn->LFO1InternalDepth; pLFO1->ExtController = 0; // no external controller + bLFO1Enabled = (lfo1_internal_depth > 0); break; case ::gig::lfo1_ctrl_modwheel: lfo1_internal_depth = 0; pLFO1->ExtController = 1; // MIDI controller 1 + bLFO1Enabled = (pDimRgn->LFO1ControlDepth > 0); break; case ::gig::lfo1_ctrl_breath: lfo1_internal_depth = 0; pLFO1->ExtController = 2; // MIDI controller 2 + bLFO1Enabled = (pDimRgn->LFO1ControlDepth > 0); break; case ::gig::lfo1_ctrl_internal_modwheel: lfo1_internal_depth = pDimRgn->LFO1InternalDepth; pLFO1->ExtController = 1; // MIDI controller 1 + bLFO1Enabled = (lfo1_internal_depth > 0 || pDimRgn->LFO1ControlDepth > 0); break; case ::gig::lfo1_ctrl_internal_breath: lfo1_internal_depth = pDimRgn->LFO1InternalDepth; pLFO1->ExtController = 2; // MIDI controller 2 + bLFO1Enabled = (lfo1_internal_depth > 0 || pDimRgn->LFO1ControlDepth > 0); break; default: lfo1_internal_depth = 0; pLFO1->ExtController = 0; // no external controller + bLFO1Enabled = false; } - pLFO1->Trigger(pDimRgn->LFO1Frequency, - lfo1_internal_depth, - pDimRgn->LFO1ControlDepth, - pEngineChannel->ControllerTable[pLFO1->ExtController], - pDimRgn->LFO1FlipPhase, - pEngine->SampleRate, - Delay); + if (bLFO1Enabled) pLFO1->trigger(pDimRgn->LFO1Frequency, + start_level_max, + lfo1_internal_depth, + pDimRgn->LFO1ControlDepth, + pDimRgn->LFO1FlipPhase, + pEngine->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE); } @@ -468,34 +325,39 @@ case ::gig::lfo2_ctrl_internal: lfo2_internal_depth = pDimRgn->LFO2InternalDepth; pLFO2->ExtController = 0; // no external controller + bLFO2Enabled = (lfo2_internal_depth > 0); break; case ::gig::lfo2_ctrl_modwheel: lfo2_internal_depth = 0; pLFO2->ExtController = 1; // MIDI controller 1 + bLFO2Enabled = (pDimRgn->LFO2ControlDepth > 0); break; case ::gig::lfo2_ctrl_foot: lfo2_internal_depth = 0; pLFO2->ExtController = 4; // MIDI controller 4 + bLFO2Enabled = (pDimRgn->LFO2ControlDepth > 0); break; case ::gig::lfo2_ctrl_internal_modwheel: lfo2_internal_depth = pDimRgn->LFO2InternalDepth; pLFO2->ExtController = 1; // MIDI controller 1 + bLFO2Enabled = (lfo2_internal_depth > 0 || pDimRgn->LFO2ControlDepth > 0); break; case ::gig::lfo2_ctrl_internal_foot: lfo2_internal_depth = pDimRgn->LFO2InternalDepth; pLFO2->ExtController = 4; // MIDI controller 4 + bLFO2Enabled = (lfo2_internal_depth > 0 || pDimRgn->LFO2ControlDepth > 0); break; default: lfo2_internal_depth = 0; pLFO2->ExtController = 0; // no external controller + bLFO2Enabled = false; } - pLFO2->Trigger(pDimRgn->LFO2Frequency, - lfo2_internal_depth, - pDimRgn->LFO2ControlDepth, - pEngineChannel->ControllerTable[pLFO2->ExtController], - pDimRgn->LFO2FlipPhase, - pEngine->SampleRate, - Delay); + if (bLFO2Enabled) pLFO2->trigger(pDimRgn->LFO2Frequency, + start_level_max, + lfo2_internal_depth, + pDimRgn->LFO2ControlDepth, + pDimRgn->LFO2FlipPhase, + pEngine->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE); } @@ -506,34 +368,39 @@ case ::gig::lfo3_ctrl_internal: lfo3_internal_depth = pDimRgn->LFO3InternalDepth; pLFO3->ExtController = 0; // no external controller + bLFO3Enabled = (lfo3_internal_depth > 0); break; case ::gig::lfo3_ctrl_modwheel: lfo3_internal_depth = 0; pLFO3->ExtController = 1; // MIDI controller 1 + bLFO3Enabled = (pDimRgn->LFO3ControlDepth > 0); break; case ::gig::lfo3_ctrl_aftertouch: lfo3_internal_depth = 0; pLFO3->ExtController = 0; // TODO: aftertouch not implemented yet + bLFO3Enabled = false; // see TODO comment in line above break; case ::gig::lfo3_ctrl_internal_modwheel: lfo3_internal_depth = pDimRgn->LFO3InternalDepth; pLFO3->ExtController = 1; // MIDI controller 1 + bLFO3Enabled = (lfo3_internal_depth > 0 || pDimRgn->LFO3ControlDepth > 0); break; case ::gig::lfo3_ctrl_internal_aftertouch: lfo3_internal_depth = pDimRgn->LFO3InternalDepth; pLFO1->ExtController = 0; // TODO: aftertouch not implemented yet + bLFO3Enabled = (lfo3_internal_depth > 0 /*|| pDimRgn->LFO3ControlDepth > 0*/); // see TODO comment in line above break; default: lfo3_internal_depth = 0; pLFO3->ExtController = 0; // no external controller + bLFO3Enabled = false; } - pLFO3->Trigger(pDimRgn->LFO3Frequency, - lfo3_internal_depth, - pDimRgn->LFO3ControlDepth, - pEngineChannel->ControllerTable[pLFO3->ExtController], - false, - pEngine->SampleRate, - Delay); + if (bLFO3Enabled) pLFO3->trigger(pDimRgn->LFO3Frequency, + start_level_mid, + lfo3_internal_depth, + pDimRgn->LFO3ControlDepth, + false, + pEngine->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE); } @@ -606,8 +473,8 @@ #endif // CONFIG_OVERRIDE_RESONANCE_CTRL #ifndef CONFIG_OVERRIDE_FILTER_TYPE - FilterLeft.SetType(pDimRgn->VCFType); - FilterRight.SetType(pDimRgn->VCFType); + finalSynthesisParameters.filterLeft.SetType(pDimRgn->VCFType); + finalSynthesisParameters.filterRight.SetType(pDimRgn->VCFType); #else // override filter type FilterLeft.SetType(CONFIG_OVERRIDE_FILTER_TYPE); FilterRight.SetType(CONFIG_OVERRIDE_FILTER_TYPE); @@ -617,21 +484,32 @@ VCFResonanceCtrl.value = pEngineChannel->ControllerTable[VCFResonanceCtrl.controller]; // calculate cutoff frequency - float cutoff = (!VCFCutoffCtrl.controller) - ? exp((float) (127 - itNoteOnEvent->Param.Note.Velocity) * (float) pDimRgn->VCFVelocityScale * 6.2E-5f * FILTER_CUTOFF_COEFF) * CONFIG_FILTER_CUTOFF_MAX - : exp((float) VCFCutoffCtrl.value * 0.00787402f * FILTER_CUTOFF_COEFF) * CONFIG_FILTER_CUTOFF_MAX; - - // calculate resonance - float resonance = (float) VCFResonanceCtrl.value * 0.00787f; // 0.0..1.0 + float cutoff = pDimRgn->GetVelocityCutoff(itNoteOnEvent->Param.Note.Velocity); if (pDimRgn->VCFKeyboardTracking) { - resonance += (float) (itNoteOnEvent->Param.Note.Key - pDimRgn->VCFKeyboardTrackingBreakpoint) * 0.00787f; + cutoff *= exp((itNoteOnEvent->Param.Note.Key - pDimRgn->VCFKeyboardTrackingBreakpoint) * 0.057762265f); // (ln(2) / 12) } - Constrain(resonance, 0.0, 1.0); // correct resonance if outside allowed value range (0.0..1.0) + CutoffBase = cutoff; - VCFCutoffCtrl.fvalue = cutoff - CONFIG_FILTER_CUTOFF_MIN; - VCFResonanceCtrl.fvalue = resonance; + int cvalue; + if (VCFCutoffCtrl.controller) { + cvalue = pEngineChannel->ControllerTable[VCFCutoffCtrl.controller]; + if (pDimRgn->VCFCutoffControllerInvert) cvalue = 127 - cvalue; + // VCFVelocityScale in this case means Minimum cutoff + if (cvalue < pDimRgn->VCFVelocityScale) cvalue = pDimRgn->VCFVelocityScale; + } + else { + cvalue = pDimRgn->VCFCutoff; + } + cutoff *= float(cvalue) * 0.00787402f; // (1 / 127) + if (cutoff > 1.0) cutoff = 1.0; + cutoff = (cutoff < 0.5 ? cutoff * 4826 - 1 : cutoff * 5715 - 449); + if (cutoff < 1.0) cutoff = 1.0; + + // calculate resonance + float resonance = (float) (VCFResonanceCtrl.controller ? VCFResonanceCtrl.value : pDimRgn->VCFResonance) * 0.00787f; // 0.0..1.0 - FilterUpdateCounter = -1; + VCFCutoffCtrl.fvalue = cutoff - 1.0; + VCFResonanceCtrl.fvalue = resonance; } else { VCFCutoffCtrl.controller = 0; @@ -655,37 +533,8 @@ void Voice::Render(uint Samples) { // select default values for synthesis mode bits - SYNTHESIS_MODE_SET_INTERPOLATE(SynthesisMode, (PitchBase * PitchBend) != 1.0f); - SYNTHESIS_MODE_SET_CONSTPITCH(SynthesisMode, true); SYNTHESIS_MODE_SET_LOOP(SynthesisMode, false); - // Reset the synthesis parameter matrix - - pEngine->ResetSynthesisParameters(Event::destination_vca, this->Volume * this->CrossfadeVolume * pEngineChannel->GlobalVolume); - pEngine->ResetSynthesisParameters(Event::destination_vco, this->PitchBase); - pEngine->ResetSynthesisParameters(Event::destination_vcfc, VCFCutoffCtrl.fvalue); - pEngine->ResetSynthesisParameters(Event::destination_vcfr, VCFResonanceCtrl.fvalue); - - // Apply events to the synthesis parameter matrix - ProcessEvents(Samples); - - // Let all modulators write their parameter changes to the synthesis parameter matrix for the current audio fragment - pEG1->Process(Samples, pEngineChannel->pMIDIKeyInfo[MIDIKey].pEvents, itTriggerEvent, this->Pos, this->PitchBase * this->PitchBend, itKillEvent); - pEG2->Process(Samples, pEngineChannel->pMIDIKeyInfo[MIDIKey].pEvents, itTriggerEvent, this->Pos, this->PitchBase * this->PitchBend); - if (pEG3->Process(Samples)) { // if pitch EG is active - SYNTHESIS_MODE_SET_INTERPOLATE(SynthesisMode, true); - SYNTHESIS_MODE_SET_CONSTPITCH(SynthesisMode, false); - } - pLFO1->Process(Samples); - pLFO2->Process(Samples); - if (pLFO3->Process(Samples)) { // if pitch LFO modulation is active - SYNTHESIS_MODE_SET_INTERPOLATE(SynthesisMode, true); - SYNTHESIS_MODE_SET_CONSTPITCH(SynthesisMode, false); - } - - if (SYNTHESIS_MODE_GET_FILTER(SynthesisMode)) - CalculateBiquadParameters(Samples); // calculate the final biquad filter parameters - switch (this->PlaybackState) { case playback_state_init: @@ -700,12 +549,11 @@ if (DiskVoice) { // check if we reached the allowed limit of the sample RAM cache - if (Pos > MaxRAMPos) { - dmsg(5,("Voice: switching to disk playback (Pos=%f)\n", Pos)); + if (finalSynthesisParameters.dPos > MaxRAMPos) { + dmsg(5,("Voice: switching to disk playback (Pos=%f)\n", finalSynthesisParameters.dPos)); this->PlaybackState = playback_state_disk; } - } - else if (Pos >= pSample->GetCache().Size / pSample->FrameSize) { + } else if (finalSynthesisParameters.dPos >= pSample->GetCache().Size / pSample->FrameSize) { this->PlaybackState = playback_state_end; } } @@ -720,8 +568,8 @@ KillImmediately(); return; } - DiskStreamRef.pStream->IncrementReadPos(pSample->Channels * (int(Pos) - MaxRAMPos)); - Pos -= int(Pos); + DiskStreamRef.pStream->IncrementReadPos(pSample->Channels * (int(finalSynthesisParameters.dPos) - MaxRAMPos)); + finalSynthesisParameters.dPos -= int(finalSynthesisParameters.dPos); RealSampleWordsLeftToRead = -1; // -1 means no silence has been added yet } @@ -742,10 +590,10 @@ // render current audio fragment Synthesize(Samples, ptr, Delay); - const int iPos = (int) Pos; + const int iPos = (int) finalSynthesisParameters.dPos; const int readSampleWords = iPos * pSample->Channels; // amount of sample words actually been read DiskStreamRef.pStream->IncrementReadPos(readSampleWords); - Pos -= iPos; // just keep fractional part of Pos + finalSynthesisParameters.dPos -= iPos; // just keep fractional part of playback position // change state of voice to 'end' if we really reached the end of the sample data if (RealSampleWordsLeftToRead >= 0) { @@ -760,18 +608,13 @@ break; } - // Reset synthesis event lists (except VCO, as VCO events apply channel wide currently) - pEngineChannel->pSynthesisEvents[Event::destination_vca]->clear(); - pEngineChannel->pSynthesisEvents[Event::destination_vcfc]->clear(); - pEngineChannel->pSynthesisEvents[Event::destination_vcfr]->clear(); - // Reset delay Delay = 0; itTriggerEvent = Pool::Iterator(); // If sample stream or release stage finished, kill the voice - if (PlaybackState == playback_state_end || pEG1->GetStage() == EGADSR::stage_end) KillImmediately(); + if (PlaybackState == playback_state_end || EG1.getSegmentType() == EGADSR::segment_end) KillImmediately(); } /** @@ -779,11 +622,8 @@ * suspended / not running. */ void Voice::Reset() { - pLFO1->Reset(); - pLFO2->Reset(); - pLFO3->Reset(); - FilterLeft.Reset(); - FilterRight.Reset(); + finalSynthesisParameters.filterLeft.Reset(); + finalSynthesisParameters.filterRight.Reset(); DiskStreamRef.pStream = NULL; DiskStreamRef.hStream = 0; DiskStreamRef.State = Stream::state_unused; @@ -794,231 +634,228 @@ } /** - * Process the control change event lists of the engine for the current - * audio fragment. Event values will be applied to the synthesis parameter - * matrix. + * Process given list of MIDI note on, note off and sustain pedal events + * for the given time. * - * @param Samples - number of samples to be rendered in this audio fragment cycle + * @param itEvent - iterator pointing to the next event to be processed + * @param End - youngest time stamp where processing should be stopped */ - void Voice::ProcessEvents(uint Samples) { + void Voice::processTransitionEvents(RTList::Iterator& itEvent, uint End) { + for (; itEvent && itEvent->FragmentPos() <= End; ++itEvent) { + if (itEvent->Type == Event::type_release) { + EG1.update(EGADSR::event_release, pEngine->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE); + EG2.update(EGADSR::event_release, pEngine->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE); + } else if (itEvent->Type == Event::type_cancel_release) { + EG1.update(EGADSR::event_cancel_release, pEngine->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE); + EG2.update(EGADSR::event_cancel_release, pEngine->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE); + } + } + } - // dispatch control change events - RTList::Iterator itCCEvent = pEngineChannel->pCCEvents->first(); - if (Delay) { // skip events that happened before this voice was triggered - while (itCCEvent && itCCEvent->FragmentPos() <= Delay) ++itCCEvent; - } - while (itCCEvent) { - if (itCCEvent->Param.CC.Controller) { // if valid MIDI controller - if (itCCEvent->Param.CC.Controller == VCFCutoffCtrl.controller) { - *pEngineChannel->pSynthesisEvents[Event::destination_vcfc]->allocAppend() = *itCCEvent; - } - if (itCCEvent->Param.CC.Controller == VCFResonanceCtrl.controller) { - *pEngineChannel->pSynthesisEvents[Event::destination_vcfr]->allocAppend() = *itCCEvent; + /** + * Process given list of MIDI control change and pitch bend events for + * the given time. + * + * @param itEvent - iterator pointing to the next event to be processed + * @param End - youngest time stamp where processing should be stopped + */ + void Voice::processCCEvents(RTList::Iterator& itEvent, uint End) { + for (; itEvent && itEvent->FragmentPos() <= End; ++itEvent) { + if (itEvent->Type == Event::type_control_change && + itEvent->Param.CC.Controller) { // if (valid) MIDI control change event + if (itEvent->Param.CC.Controller == VCFCutoffCtrl.controller) { + processCutoffEvent(itEvent); + } + if (itEvent->Param.CC.Controller == VCFResonanceCtrl.controller) { + processResonanceEvent(itEvent); } - if (itCCEvent->Param.CC.Controller == pLFO1->ExtController) { - pLFO1->SendEvent(itCCEvent); + if (itEvent->Param.CC.Controller == pLFO1->ExtController) { + pLFO1->update(itEvent->Param.CC.Value); } - if (itCCEvent->Param.CC.Controller == pLFO2->ExtController) { - pLFO2->SendEvent(itCCEvent); + if (itEvent->Param.CC.Controller == pLFO2->ExtController) { + pLFO2->update(itEvent->Param.CC.Value); } - if (itCCEvent->Param.CC.Controller == pLFO3->ExtController) { - pLFO3->SendEvent(itCCEvent); + if (itEvent->Param.CC.Controller == pLFO3->ExtController) { + pLFO3->update(itEvent->Param.CC.Value); } if (pDimRgn->AttenuationController.type == ::gig::attenuation_ctrl_t::type_controlchange && - itCCEvent->Param.CC.Controller == pDimRgn->AttenuationController.controller_number) { // if crossfade event - *pEngineChannel->pSynthesisEvents[Event::destination_vca]->allocAppend() = *itCCEvent; + itEvent->Param.CC.Controller == pDimRgn->AttenuationController.controller_number) { + processCrossFadeEvent(itEvent); } + } else if (itEvent->Type == Event::type_pitchbend) { // if pitch bend event + processPitchEvent(itEvent); } + } + } + + void Voice::processPitchEvent(RTList::Iterator& itEvent) { + const float pitch = RTMath::CentsToFreqRatio(((double) itEvent->Param.Pitch.Pitch / 8192.0) * 200.0); // +-two semitones = +-200 cents + finalSynthesisParameters.fFinalPitch *= pitch; + PitchBend = pitch; + } - ++itCCEvent; + void Voice::processCrossFadeEvent(RTList::Iterator& itEvent) { + CrossfadeVolume = CrossfadeAttenuation(itEvent->Param.CC.Value); + #if CONFIG_PROCESS_MUTED_CHANNELS + const float effectiveVolume = CrossfadeVolume * Volume * (pEngineChannel->GetMute() ? 0 : pEngineChannel->GlobalVolume); + #else + const float effectiveVolume = CrossfadeVolume * Volume * pEngineChannel->GlobalVolume; + #endif + fFinalVolume = effectiveVolume; + } + + void Voice::processCutoffEvent(RTList::Iterator& itEvent) { + int ccvalue = itEvent->Param.CC.Value; + if (VCFCutoffCtrl.value == ccvalue) return; + VCFCutoffCtrl.value == ccvalue; + if (pDimRgn->VCFCutoffControllerInvert) ccvalue = 127 - ccvalue; + if (ccvalue < pDimRgn->VCFVelocityScale) ccvalue = pDimRgn->VCFVelocityScale; + float cutoff = CutoffBase * float(ccvalue) * 0.00787402f; // (1 / 127) + if (cutoff > 1.0) cutoff = 1.0; + cutoff = (cutoff < 0.5 ? cutoff * 4826 - 1 : cutoff * 5715 - 449); + if (cutoff < 1.0) cutoff = 1.0; + + VCFCutoffCtrl.fvalue = cutoff - 1.0; // needed for initialization of fFinalCutoff next time + fFinalCutoff = cutoff; + } + + void Voice::processResonanceEvent(RTList::Iterator& itEvent) { + // convert absolute controller value to differential + const int ctrldelta = itEvent->Param.CC.Value - VCFResonanceCtrl.value; + VCFResonanceCtrl.value = itEvent->Param.CC.Value; + const float resonancedelta = (float) ctrldelta * 0.00787f; // 0.0..1.0 + fFinalResonance += resonancedelta; + // needed for initialization of parameter + VCFResonanceCtrl.fvalue = itEvent->Param.CC.Value * 0.00787f; + } + + /** + * Synthesizes the current audio fragment for this voice. + * + * @param Samples - number of sample points to be rendered in this audio + * fragment cycle + * @param pSrc - pointer to input sample data + * @param Skip - number of sample points to skip in output buffer + */ + void Voice::Synthesize(uint Samples, sample_t* pSrc, uint Skip) { + finalSynthesisParameters.pOutLeft = &pEngineChannel->pOutputLeft[Skip]; + finalSynthesisParameters.pOutRight = &pEngineChannel->pOutputRight[Skip]; + finalSynthesisParameters.pSrc = pSrc; + + RTList::Iterator itCCEvent = pEngineChannel->pEvents->first(); + RTList::Iterator itNoteEvent = pEngineChannel->pMIDIKeyInfo[MIDIKey].pEvents->first(); + + if (Skip) { // skip events that happened before this voice was triggered + while (itCCEvent && itCCEvent->FragmentPos() <= Skip) ++itCCEvent; + while (itNoteEvent && itNoteEvent->FragmentPos() <= Skip) ++itNoteEvent; } + uint killPos; + if (itKillEvent) killPos = RTMath::Min(itKillEvent->FragmentPos(), pEngine->MaxFadeOutPos); - // process pitch events - { - RTList* pVCOEventList = pEngineChannel->pSynthesisEvents[Event::destination_vco]; - RTList::Iterator itVCOEvent = pVCOEventList->first(); - if (Delay) { // skip events that happened before this voice was triggered - while (itVCOEvent && itVCOEvent->FragmentPos() <= Delay) ++itVCOEvent; - } - // apply old pitchbend value until first pitch event occurs - if (this->PitchBend != 1.0) { - uint end = (itVCOEvent) ? itVCOEvent->FragmentPos() : Samples; - for (uint i = Delay; i < end; i++) { - pEngine->pSynthesisParameters[Event::destination_vco][i] *= this->PitchBend; - } + uint i = Skip; + while (i < Samples) { + int iSubFragmentEnd = RTMath::Min(i + CONFIG_DEFAULT_SUBFRAGMENT_SIZE, Samples); + + // initialize all final synthesis parameters + finalSynthesisParameters.fFinalPitch = PitchBase * PitchBend; + #if CONFIG_PROCESS_MUTED_CHANNELS + fFinalVolume = this->Volume * this->CrossfadeVolume * (pEngineChannel->GetMute() ? 0 : pEngineChannel->GlobalVolume); + #else + fFinalVolume = this->Volume * this->CrossfadeVolume * pEngineChannel->GlobalVolume; + #endif + fFinalCutoff = VCFCutoffCtrl.fvalue; + fFinalResonance = VCFResonanceCtrl.fvalue; + + // process MIDI control change and pitchbend events for this subfragment + processCCEvents(itCCEvent, iSubFragmentEnd); + + // process transition events (note on, note off & sustain pedal) + processTransitionEvents(itNoteEvent, iSubFragmentEnd); + + // if the voice was killed in this subfragment switch EG1 to fade out stage + if (itKillEvent && killPos <= iSubFragmentEnd) { + EG1.enterFadeOutStage(); + itKillEvent = Pool::Iterator(); } - float pitch; - while (itVCOEvent) { - RTList::Iterator itNextVCOEvent = itVCOEvent; - ++itNextVCOEvent; - - // calculate the influence length of this event (in sample points) - uint end = (itNextVCOEvent) ? itNextVCOEvent->FragmentPos() : Samples; - - pitch = RTMath::CentsToFreqRatio(((double) itVCOEvent->Param.Pitch.Pitch / 8192.0) * 200.0); // +-two semitones = +-200 cents - - // apply pitch value to the pitch parameter sequence - for (uint i = itVCOEvent->FragmentPos(); i < end; i++) { - pEngine->pSynthesisParameters[Event::destination_vco][i] *= pitch; - } - itVCOEvent = itNextVCOEvent; + // process envelope generators + switch (EG1.getSegmentType()) { + case EGADSR::segment_lin: + fFinalVolume *= EG1.processLin(); + break; + case EGADSR::segment_exp: + fFinalVolume *= EG1.processExp(); + break; + case EGADSR::segment_end: + fFinalVolume *= EG1.getLevel(); + break; // noop } - if (!pVCOEventList->isEmpty()) { - this->PitchBend = pitch; - SYNTHESIS_MODE_SET_INTERPOLATE(SynthesisMode, true); - SYNTHESIS_MODE_SET_CONSTPITCH(SynthesisMode, false); + switch (EG2.getSegmentType()) { + case EGADSR::segment_lin: + fFinalCutoff *= EG2.processLin(); + break; + case EGADSR::segment_exp: + fFinalCutoff *= EG2.processExp(); + break; + case EGADSR::segment_end: + fFinalCutoff *= EG2.getLevel(); + break; // noop } - } + if (EG3.active()) finalSynthesisParameters.fFinalPitch *= RTMath::CentsToFreqRatio(EG3.render()); - // process volume / attenuation events (TODO: we only handle and _expect_ crossfade events here ATM !) - { - RTList* pVCAEventList = pEngineChannel->pSynthesisEvents[Event::destination_vca]; - RTList::Iterator itVCAEvent = pVCAEventList->first(); - if (Delay) { // skip events that happened before this voice was triggered - while (itVCAEvent && itVCAEvent->FragmentPos() <= Delay) ++itVCAEvent; - } - float crossfadevolume; - while (itVCAEvent) { - RTList::Iterator itNextVCAEvent = itVCAEvent; - ++itNextVCAEvent; - - // calculate the influence length of this event (in sample points) - uint end = (itNextVCAEvent) ? itNextVCAEvent->FragmentPos() : Samples; - - crossfadevolume = CrossfadeAttenuation(itVCAEvent->Param.CC.Value); - - float effective_volume = crossfadevolume * this->Volume * pEngineChannel->GlobalVolume; - - // apply volume value to the volume parameter sequence - for (uint i = itVCAEvent->FragmentPos(); i < end; i++) { - pEngine->pSynthesisParameters[Event::destination_vca][i] = effective_volume; - } + // process low frequency oscillators + if (bLFO1Enabled) fFinalVolume *= pLFO1->render(); + if (bLFO2Enabled) fFinalCutoff *= pLFO2->render(); + if (bLFO3Enabled) finalSynthesisParameters.fFinalPitch *= RTMath::CentsToFreqRatio(pLFO3->render()); - itVCAEvent = itNextVCAEvent; + // if filter enabled then update filter coefficients + if (SYNTHESIS_MODE_GET_FILTER(SynthesisMode)) { + finalSynthesisParameters.filterLeft.SetParameters(fFinalCutoff + 1.0, fFinalResonance, pEngine->SampleRate); + finalSynthesisParameters.filterRight.SetParameters(fFinalCutoff + 1.0, fFinalResonance, pEngine->SampleRate); } - if (!pVCAEventList->isEmpty()) this->CrossfadeVolume = crossfadevolume; - } - // process filter cutoff events - { - RTList* pCutoffEventList = pEngineChannel->pSynthesisEvents[Event::destination_vcfc]; - RTList::Iterator itCutoffEvent = pCutoffEventList->first(); - if (Delay) { // skip events that happened before this voice was triggered - while (itCutoffEvent && itCutoffEvent->FragmentPos() <= Delay) ++itCutoffEvent; - } - float cutoff; - while (itCutoffEvent) { - RTList::Iterator itNextCutoffEvent = itCutoffEvent; - ++itNextCutoffEvent; - - // calculate the influence length of this event (in sample points) - uint end = (itNextCutoffEvent) ? itNextCutoffEvent->FragmentPos() : Samples; - - cutoff = exp((float) itCutoffEvent->Param.CC.Value * 0.00787402f * FILTER_CUTOFF_COEFF) * CONFIG_FILTER_CUTOFF_MAX - CONFIG_FILTER_CUTOFF_MIN; - - // apply cutoff frequency to the cutoff parameter sequence - for (uint i = itCutoffEvent->FragmentPos(); i < end; i++) { - pEngine->pSynthesisParameters[Event::destination_vcfc][i] = cutoff; - } + // do we need resampling? + const float __PLUS_ONE_CENT = 1.000577789506554859250142541782224725466f; + const float __MINUS_ONE_CENT = 0.9994225441413807496009516495583113737666f; + const bool bResamplingRequired = !(finalSynthesisParameters.fFinalPitch <= __PLUS_ONE_CENT && + finalSynthesisParameters.fFinalPitch >= __MINUS_ONE_CENT); + SYNTHESIS_MODE_SET_INTERPOLATE(SynthesisMode, bResamplingRequired); - itCutoffEvent = itNextCutoffEvent; - } - if (!pCutoffEventList->isEmpty()) VCFCutoffCtrl.fvalue = cutoff; // needed for initialization of parameter matrix next time - } + // prepare final synthesis parameters structure + finalSynthesisParameters.fFinalVolumeLeft = fFinalVolume * PanLeft; + finalSynthesisParameters.fFinalVolumeRight = fFinalVolume * PanRight; + finalSynthesisParameters.uiToGo = iSubFragmentEnd - i; - // process filter resonance events - { - RTList* pResonanceEventList = pEngineChannel->pSynthesisEvents[Event::destination_vcfr]; - RTList::Iterator itResonanceEvent = pResonanceEventList->first(); - if (Delay) { // skip events that happened before this voice was triggered - while (itResonanceEvent && itResonanceEvent->FragmentPos() <= Delay) ++itResonanceEvent; - } - while (itResonanceEvent) { - RTList::Iterator itNextResonanceEvent = itResonanceEvent; - ++itNextResonanceEvent; - - // calculate the influence length of this event (in sample points) - uint end = (itNextResonanceEvent) ? itNextResonanceEvent->FragmentPos() : Samples; - - // convert absolute controller value to differential - int ctrldelta = itResonanceEvent->Param.CC.Value - VCFResonanceCtrl.value; - VCFResonanceCtrl.value = itResonanceEvent->Param.CC.Value; - - float resonancedelta = (float) ctrldelta * 0.00787f; // 0.0..1.0 - - // apply cutoff frequency to the cutoff parameter sequence - for (uint i = itResonanceEvent->FragmentPos(); i < end; i++) { - pEngine->pSynthesisParameters[Event::destination_vcfr][i] += resonancedelta; - } + // render audio for one subfragment + RunSynthesisFunction(SynthesisMode, &finalSynthesisParameters, &loop); - itResonanceEvent = itNextResonanceEvent; - } - if (!pResonanceEventList->isEmpty()) VCFResonanceCtrl.fvalue = pResonanceEventList->last()->Param.CC.Value * 0.00787f; // needed for initialization of parameter matrix next time - } - } + const double newPos = Pos + (iSubFragmentEnd - i) * finalSynthesisParameters.fFinalPitch; - /** - * Calculate all necessary, final biquad filter parameters. - * - * @param Samples - number of samples to be rendered in this audio fragment cycle - */ - void Voice::CalculateBiquadParameters(uint Samples) { - biquad_param_t bqbase; - biquad_param_t bqmain; - float prev_cutoff = pEngine->pSynthesisParameters[Event::destination_vcfc][0]; - float prev_res = pEngine->pSynthesisParameters[Event::destination_vcfr][0]; - FilterLeft.SetParameters( &bqbase, &bqmain, prev_cutoff + CONFIG_FILTER_CUTOFF_MIN, prev_res, pEngine->SampleRate); - FilterRight.SetParameters(&bqbase, &bqmain, prev_cutoff + CONFIG_FILTER_CUTOFF_MIN, prev_res, pEngine->SampleRate); - pEngine->pBasicFilterParameters[0] = bqbase; - pEngine->pMainFilterParameters[0] = bqmain; - - float* bq; - for (int i = 1; i < Samples; i++) { - // recalculate biquad parameters if cutoff or resonance differ from previous sample point - if (!(i & FILTER_UPDATE_MASK)) { - if (pEngine->pSynthesisParameters[Event::destination_vcfr][i] != prev_res || - pEngine->pSynthesisParameters[Event::destination_vcfc][i] != prev_cutoff) - { - prev_cutoff = pEngine->pSynthesisParameters[Event::destination_vcfc][i]; - prev_res = pEngine->pSynthesisParameters[Event::destination_vcfr][i]; - FilterLeft.SetParameters( &bqbase, &bqmain, prev_cutoff + CONFIG_FILTER_CUTOFF_MIN, prev_res, pEngine->SampleRate); - FilterRight.SetParameters(&bqbase, &bqmain, prev_cutoff + CONFIG_FILTER_CUTOFF_MIN, prev_res, pEngine->SampleRate); + // increment envelopes' positions + if (EG1.active()) { + + // 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 + if (pSample->Loops && Pos <= pSample->LoopStart && pSample->LoopStart < newPos) { + EG1.update(EGADSR::event_hold_end, pEngine->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE); } + + EG1.increment(1); + if (!EG1.toStageEndLeft()) EG1.update(EGADSR::event_stage_end, pEngine->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE); } + if (EG2.active()) { + EG2.increment(1); + if (!EG2.toStageEndLeft()) EG2.update(EGADSR::event_stage_end, pEngine->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE); + } + EG3.increment(1); + if (!EG3.toEndLeft()) EG3.update(); // neutralize envelope coefficient if end reached - //same as 'pEngine->pBasicFilterParameters[i] = bqbase;' - bq = (float*) &pEngine->pBasicFilterParameters[i]; - bq[0] = bqbase.b0; - bq[1] = bqbase.b1; - bq[2] = bqbase.b2; - bq[3] = bqbase.a1; - bq[4] = bqbase.a2; - - // same as 'pEngine->pMainFilterParameters[i] = bqmain;' - bq = (float*) &pEngine->pMainFilterParameters[i]; - bq[0] = bqmain.b0; - bq[1] = bqmain.b1; - bq[2] = bqmain.b2; - bq[3] = bqmain.a1; - bq[4] = bqmain.a2; + Pos = newPos; + i = iSubFragmentEnd; } } /** - * Synthesizes the current audio fragment for this voice. - * - * @param Samples - number of sample points to be rendered in this audio - * fragment cycle - * @param pSrc - pointer to input sample data - * @param Skip - number of sample points to skip in output buffer - */ - void Voice::Synthesize(uint Samples, sample_t* pSrc, uint Skip) { - RunSynthesisFunction(SynthesisMode, *this, Samples, pSrc, Skip); - } - - /** * Immediately kill the voice. This method should not be used to kill * a normal, active voice, because it doesn't take care of things like * fading down the volume level to avoid clicks and regular processing