--- linuxsampler/trunk/src/engines/gig/Voice.cpp 2004/12/21 04:54:37 325 +++ linuxsampler/trunk/src/engines/gig/Voice.cpp 2005/03/14 22:35:44 460 @@ -3,6 +3,7 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * + * Copyright (C) 2005 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -58,10 +59,17 @@ pLFO2 = NULL; pLFO3 = NULL; KeyGroup = 0; - + SynthesisMode = 0; // set all mode bits to 0 first // select synthesis implementation (currently either pure C++ or MMX+SSE(1)) + #if ARCH_X86 SYNTHESIS_MODE_SET_IMPLEMENTATION(SynthesisMode, Features::supportsMMX() && Features::supportsSSE()); + #else + SYNTHESIS_MODE_SET_IMPLEMENTATION(SynthesisMode, false); + #endif SYNTHESIS_MODE_SET_PROFILING(SynthesisMode, true); + + FilterLeft.Reset(); + FilterRight.Reset(); } Voice::~Voice() { @@ -109,15 +117,19 @@ * Initializes and triggers the voice, a disk stream will be launched if * needed. * - * @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 VoiceStealing - wether the voice is allowed to steal voices for further subvoices - * @returns 0 on success, a value < 0 if something failed + * @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 + * @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(Pool::Iterator& itNoteOnEvent, int PitchBend, ::gig::Instrument* pInstrument, int iLayer, bool ReleaseTriggerVoice, bool VoiceStealing) { + int Voice::Trigger(EngineChannel* pEngineChannel, Pool::Iterator& itNoteOnEvent, int PitchBend, ::gig::Instrument* pInstrument, int iLayer, bool ReleaseTriggerVoice, bool VoiceStealingAllowed) { + this->pEngineChannel = pEngineChannel; if (!pInstrument) { dmsg(1,("voice::trigger: !pInstrument\n")); exit(EXIT_FAILURE); @@ -133,19 +145,19 @@ Delay = itNoteOnEvent->FragmentPos(); itTriggerEvent = itNoteOnEvent; itKillEvent = Pool::Iterator(); - itChildVoice = Pool::Iterator(); if (!pRegion) { - std::cerr << "gig::Voice: No Region defined for MIDI key " << MIDIKey << std::endl << std::flush; - KillImmediately(); + dmsg(4, ("gig::Voice: No Region defined for MIDI key %d\n", MIDIKey)); return -1; } - KeyGroup = pRegion->KeyGroup; + // 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; // 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[5] = {0,0,0,0,0}; + uint DimValues[8] = { 0 }; for (int i = pRegion->Dimensions - 1; i >= 0; i--) { switch (pRegion->pDimensionDefinitions[i].dimension) { case ::gig::dimension_samplechannel: @@ -153,10 +165,6 @@ break; case ::gig::dimension_layer: DimValues[i] = iLayer; - // if this is the 1st layer then spawn further voices for all the other layers - if (iLayer == 0) - for (int iNewLayer = 1; iNewLayer < pRegion->pDimensionDefinitions[i].zones; iNewLayer++) - itChildVoice = pEngine->LaunchVoice(itNoteOnEvent, iNewLayer, ReleaseTriggerVoice, VoiceStealing); break; case ::gig::dimension_velocity: DimValues[i] = itNoteOnEvent->Param.Note.Velocity; @@ -169,76 +177,83 @@ DimValues[i] = (uint) ReleaseTriggerVoice; break; case ::gig::dimension_keyboard: - DimValues[i] = (uint) itNoteOnEvent->Param.Note.Key; + 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] = pEngine->ControllerTable[1]; + DimValues[i] = pEngineChannel->ControllerTable[1]; break; case ::gig::dimension_breath: - DimValues[i] = pEngine->ControllerTable[2]; + DimValues[i] = pEngineChannel->ControllerTable[2]; break; case ::gig::dimension_foot: - DimValues[i] = pEngine->ControllerTable[4]; + DimValues[i] = pEngineChannel->ControllerTable[4]; break; case ::gig::dimension_portamentotime: - DimValues[i] = pEngine->ControllerTable[5]; + DimValues[i] = pEngineChannel->ControllerTable[5]; break; case ::gig::dimension_effect1: - DimValues[i] = pEngine->ControllerTable[12]; + DimValues[i] = pEngineChannel->ControllerTable[12]; break; case ::gig::dimension_effect2: - DimValues[i] = pEngine->ControllerTable[13]; + DimValues[i] = pEngineChannel->ControllerTable[13]; break; case ::gig::dimension_genpurpose1: - DimValues[i] = pEngine->ControllerTable[16]; + DimValues[i] = pEngineChannel->ControllerTable[16]; break; case ::gig::dimension_genpurpose2: - DimValues[i] = pEngine->ControllerTable[17]; + DimValues[i] = pEngineChannel->ControllerTable[17]; break; case ::gig::dimension_genpurpose3: - DimValues[i] = pEngine->ControllerTable[18]; + DimValues[i] = pEngineChannel->ControllerTable[18]; break; case ::gig::dimension_genpurpose4: - DimValues[i] = pEngine->ControllerTable[19]; + DimValues[i] = pEngineChannel->ControllerTable[19]; break; case ::gig::dimension_sustainpedal: - DimValues[i] = pEngine->ControllerTable[64]; + DimValues[i] = pEngineChannel->ControllerTable[64]; break; case ::gig::dimension_portamento: - DimValues[i] = pEngine->ControllerTable[65]; + DimValues[i] = pEngineChannel->ControllerTable[65]; break; case ::gig::dimension_sostenutopedal: - DimValues[i] = pEngine->ControllerTable[66]; + DimValues[i] = pEngineChannel->ControllerTable[66]; break; case ::gig::dimension_softpedal: - DimValues[i] = pEngine->ControllerTable[67]; + DimValues[i] = pEngineChannel->ControllerTable[67]; break; case ::gig::dimension_genpurpose5: - DimValues[i] = pEngine->ControllerTable[80]; + DimValues[i] = pEngineChannel->ControllerTable[80]; break; case ::gig::dimension_genpurpose6: - DimValues[i] = pEngine->ControllerTable[81]; + DimValues[i] = pEngineChannel->ControllerTable[81]; break; case ::gig::dimension_genpurpose7: - DimValues[i] = pEngine->ControllerTable[82]; + DimValues[i] = pEngineChannel->ControllerTable[82]; break; case ::gig::dimension_genpurpose8: - DimValues[i] = pEngine->ControllerTable[83]; + DimValues[i] = pEngineChannel->ControllerTable[83]; break; case ::gig::dimension_effect1depth: - DimValues[i] = pEngine->ControllerTable[91]; + DimValues[i] = pEngineChannel->ControllerTable[91]; break; case ::gig::dimension_effect2depth: - DimValues[i] = pEngine->ControllerTable[92]; + DimValues[i] = pEngineChannel->ControllerTable[92]; break; case ::gig::dimension_effect3depth: - DimValues[i] = pEngine->ControllerTable[93]; + DimValues[i] = pEngineChannel->ControllerTable[93]; break; case ::gig::dimension_effect4depth: - DimValues[i] = pEngine->ControllerTable[94]; + DimValues[i] = pEngineChannel->ControllerTable[94]; break; case ::gig::dimension_effect5depth: - DimValues[i] = pEngine->ControllerTable[95]; + DimValues[i] = pEngineChannel->ControllerTable[95]; break; case ::gig::dimension_none: std::cerr << "gig::Voice::Trigger() Error: dimension=none\n" << std::flush; @@ -247,9 +262,10 @@ std::cerr << "gig::Voice::Trigger() Error: Unknown dimension\n" << std::flush; } } - pDimRgn = pRegion->GetDimensionRegionByValue(DimValues[4],DimValues[3],DimValues[2],DimValues[1],DimValues[0]); + pDimRgn = pRegion->GetDimensionRegionByValue(DimValues); 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 // select channel mode (mono or stereo) SYNTHESIS_MODE_SET_CHANNELS(SynthesisMode, pSample->Channels == 2); @@ -263,7 +279,7 @@ CrossfadeVolume = CrossfadeAttenuation(itNoteOnEvent->Param.Note.Velocity); break; case ::gig::attenuation_ctrl_t::type_controlchange: //FIXME: currently not sample accurate - CrossfadeVolume = CrossfadeAttenuation(pEngine->ControllerTable[pDimRgn->AttenuationController.controller_number]); + CrossfadeVolume = CrossfadeAttenuation(pEngineChannel->ControllerTable[pDimRgn->AttenuationController.controller_number]); break; case ::gig::attenuation_ctrl_t::type_none: // no crossfade defined default: @@ -309,7 +325,7 @@ // calculate initial pitch value { - double pitchbasecents = pDimRgn->FineTune * 10 + (int) pEngine->ScaleTuning[MIDIKey % 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->PitchBend = RTMath::CentsToFreqRatio(((double) PitchBend / 8192.0) * 200.0); // pitchbend wheel +-2 semitones = 200 cents @@ -317,6 +333,8 @@ 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; + // setup EG 1 (VCA EG) { // get current value of EG1 controller @@ -332,7 +350,7 @@ eg1controllervalue = itNoteOnEvent->Param.Note.Velocity; break; case ::gig::eg1_ctrl_t::type_controlchange: // MIDI control change controller - eg1controllervalue = pEngine->ControllerTable[pDimRgn->EG1Controller.controller_number]; + eg1controllervalue = pEngineChannel->ControllerTable[pDimRgn->EG1Controller.controller_number]; break; } if (pDimRgn->EG1ControllerInvert) eg1controllervalue = 127 - eg1controllervalue; @@ -351,7 +369,10 @@ pDimRgn->EG1InfiniteSustain, pDimRgn->EG1Sustain, pDimRgn->EG1Release + eg1release, - Delay); + // the SSE synthesis implementation requires + // the vca start to be 16 byte aligned + SYNTHESIS_MODE_GET_IMPLEMENTATION(SynthesisMode) ? + Delay & 0xfffffffc : Delay); } @@ -370,7 +391,7 @@ eg2controllervalue = itNoteOnEvent->Param.Note.Velocity; break; case ::gig::eg2_ctrl_t::type_controlchange: // MIDI control change controller - eg2controllervalue = pEngine->ControllerTable[pDimRgn->EG2Controller.controller_number]; + eg2controllervalue = pEngineChannel->ControllerTable[pDimRgn->EG2Controller.controller_number]; break; } if (pDimRgn->EG2ControllerInvert) eg2controllervalue = 127 - eg2controllervalue; @@ -431,7 +452,7 @@ pLFO1->Trigger(pDimRgn->LFO1Frequency, lfo1_internal_depth, pDimRgn->LFO1ControlDepth, - pEngine->ControllerTable[pLFO1->ExtController], + pEngineChannel->ControllerTable[pLFO1->ExtController], pDimRgn->LFO1FlipPhase, pEngine->SampleRate, Delay); @@ -469,7 +490,7 @@ pLFO2->Trigger(pDimRgn->LFO2Frequency, lfo2_internal_depth, pDimRgn->LFO2ControlDepth, - pEngine->ControllerTable[pLFO2->ExtController], + pEngineChannel->ControllerTable[pLFO2->ExtController], pDimRgn->LFO2FlipPhase, pEngine->SampleRate, Delay); @@ -507,7 +528,7 @@ pLFO3->Trigger(pDimRgn->LFO3Frequency, lfo3_internal_depth, pDimRgn->LFO3ControlDepth, - pEngine->ControllerTable[pLFO3->ExtController], + pEngineChannel->ControllerTable[pLFO3->ExtController], false, pEngine->SampleRate, Delay); @@ -515,11 +536,12 @@ #if FORCE_FILTER_USAGE - SYNTHESIS_MODE_SET_FILTER(SynthesisMode, true); + const bool bUseFilter = true; #else // use filter only if instrument file told so - SYNTHESIS_MODE_SET_FILTER(SynthesisMode, pDimRgn->VCFEnabled); + const bool bUseFilter = pDimRgn->VCFEnabled; #endif // FORCE_FILTER_USAGE - if (pDimRgn->VCFEnabled) { + SYNTHESIS_MODE_SET_FILTER(SynthesisMode, bUseFilter); + if (bUseFilter) { #ifdef OVERRIDE_FILTER_CUTOFF_CTRL VCFCutoffCtrl.controller = OVERRIDE_FILTER_CUTOFF_CTRL; #else // use the one defined in the instrument file @@ -589,8 +611,8 @@ FilterRight.SetType(OVERRIDE_FILTER_TYPE); #endif // OVERRIDE_FILTER_TYPE - VCFCutoffCtrl.value = pEngine->ControllerTable[VCFCutoffCtrl.controller]; - VCFResonanceCtrl.value = pEngine->ControllerTable[VCFResonanceCtrl.controller]; + VCFCutoffCtrl.value = pEngineChannel->ControllerTable[VCFCutoffCtrl.controller]; + VCFResonanceCtrl.value = pEngineChannel->ControllerTable[VCFResonanceCtrl.controller]; // calculate cutoff frequency float cutoff = (!VCFCutoffCtrl.controller) @@ -637,7 +659,7 @@ // Reset the synthesis parameter matrix - pEngine->ResetSynthesisParameters(Event::destination_vca, this->Volume * this->CrossfadeVolume * pEngine->GlobalVolume); + 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); @@ -646,8 +668,8 @@ ProcessEvents(Samples); // Let all modulators write their parameter changes to the synthesis parameter matrix for the current audio fragment - pEG1->Process(Samples, pEngine->pMIDIKeyInfo[MIDIKey].pEvents, itTriggerEvent, this->Pos, this->PitchBase * this->PitchBend, itKillEvent); - pEG2->Process(Samples, pEngine->pMIDIKeyInfo[MIDIKey].pEvents, itTriggerEvent, this->Pos, this->PitchBase * this->PitchBend); + 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); @@ -660,7 +682,7 @@ } if (SYNTHESIS_MODE_GET_FILTER(SynthesisMode)) - CalculateBiquadParameters(Samples); // calculate the final biquad filter parameters + CalculateBiquadParameters(Samples); // calculate the final biquad filter parameters switch (this->PlaybackState) { @@ -694,6 +716,7 @@ } DiskStreamRef.pStream->IncrementReadPos(pSample->Channels * (int(Pos) - MaxRAMPos)); Pos -= int(Pos); + RealSampleWordsLeftToRead = -1; // -1 means no silence has been added yet } const int sampleWordsLeftToRead = DiskStreamRef.pStream->GetReadSpace(); @@ -702,6 +725,8 @@ if (DiskStreamRef.State == Stream::state_end) { const int maxSampleWordsPerCycle = (pEngine->MaxSamplesPerCycle << MAX_PITCH) * pSample->Channels + 6; // +6 for the interpolator algorithm if (sampleWordsLeftToRead <= maxSampleWordsPerCycle) { + // remember how many sample words there are before any silence has been added + if (RealSampleWordsLeftToRead < 0) RealSampleWordsLeftToRead = sampleWordsLeftToRead; DiskStreamRef.pStream->WriteSilence(maxSampleWordsPerCycle - sampleWordsLeftToRead); } } @@ -717,7 +742,10 @@ Pos -= iPos; // just keep fractional part of Pos // change state of voice to 'end' if we really reached the end of the sample data - if (DiskStreamRef.State == Stream::state_end && readSampleWords >= sampleWordsLeftToRead) this->PlaybackState = playback_state_end; + if (RealSampleWordsLeftToRead >= 0) { + RealSampleWordsLeftToRead -= readSampleWords; + if (RealSampleWordsLeftToRead <= 0) this->PlaybackState = playback_state_end; + } } break; @@ -727,9 +755,9 @@ } // Reset synthesis event lists (except VCO, as VCO events apply channel wide currently) - pEngine->pSynthesisEvents[Event::destination_vca]->clear(); - pEngine->pSynthesisEvents[Event::destination_vcfc]->clear(); - pEngine->pSynthesisEvents[Event::destination_vcfr]->clear(); + pEngineChannel->pSynthesisEvents[Event::destination_vca]->clear(); + pEngineChannel->pSynthesisEvents[Event::destination_vcfc]->clear(); + pEngineChannel->pSynthesisEvents[Event::destination_vcfr]->clear(); // Reset delay Delay = 0; @@ -769,17 +797,17 @@ void Voice::ProcessEvents(uint Samples) { // dispatch control change events - RTList::Iterator itCCEvent = pEngine->pCCEvents->first(); + 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) { - *pEngine->pSynthesisEvents[Event::destination_vcfc]->allocAppend() = *itCCEvent; + *pEngineChannel->pSynthesisEvents[Event::destination_vcfc]->allocAppend() = *itCCEvent; } if (itCCEvent->Param.CC.Controller == VCFResonanceCtrl.controller) { - *pEngine->pSynthesisEvents[Event::destination_vcfr]->allocAppend() = *itCCEvent; + *pEngineChannel->pSynthesisEvents[Event::destination_vcfr]->allocAppend() = *itCCEvent; } if (itCCEvent->Param.CC.Controller == pLFO1->ExtController) { pLFO1->SendEvent(itCCEvent); @@ -792,7 +820,7 @@ } if (pDimRgn->AttenuationController.type == ::gig::attenuation_ctrl_t::type_controlchange && itCCEvent->Param.CC.Controller == pDimRgn->AttenuationController.controller_number) { // if crossfade event - *pEngine->pSynthesisEvents[Event::destination_vca]->allocAppend() = *itCCEvent; + *pEngineChannel->pSynthesisEvents[Event::destination_vca]->allocAppend() = *itCCEvent; } } @@ -802,7 +830,7 @@ // process pitch events { - RTList* pVCOEventList = pEngine->pSynthesisEvents[Event::destination_vco]; + 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; @@ -840,7 +868,7 @@ // process volume / attenuation events (TODO: we only handle and _expect_ crossfade events here ATM !) { - RTList* pVCAEventList = pEngine->pSynthesisEvents[Event::destination_vca]; + 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; @@ -855,7 +883,7 @@ crossfadevolume = CrossfadeAttenuation(itVCAEvent->Param.CC.Value); - float effective_volume = crossfadevolume * this->Volume * pEngine->GlobalVolume; + float effective_volume = crossfadevolume * this->Volume * pEngineChannel->GlobalVolume; // apply volume value to the volume parameter sequence for (uint i = itVCAEvent->FragmentPos(); i < end; i++) { @@ -869,7 +897,7 @@ // process filter cutoff events { - RTList* pCutoffEventList = pEngine->pSynthesisEvents[Event::destination_vcfc]; + 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; @@ -896,7 +924,7 @@ // process filter resonance events { - RTList* pResonanceEventList = pEngine->pSynthesisEvents[Event::destination_vcfr]; + 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; @@ -935,8 +963,8 @@ 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, prev_res, pEngine->SampleRate); - FilterRight.SetParameters(&bqbase, &bqmain, prev_cutoff, prev_res, pEngine->SampleRate); + FilterLeft.SetParameters( &bqbase, &bqmain, prev_cutoff + FILTER_CUTOFF_MIN, prev_res, pEngine->SampleRate); + FilterRight.SetParameters(&bqbase, &bqmain, prev_cutoff + FILTER_CUTOFF_MIN, prev_res, pEngine->SampleRate); pEngine->pBasicFilterParameters[0] = bqbase; pEngine->pMainFilterParameters[0] = bqmain; @@ -949,8 +977,8 @@ { prev_cutoff = pEngine->pSynthesisParameters[Event::destination_vcfc][i]; prev_res = pEngine->pSynthesisParameters[Event::destination_vcfr][i]; - FilterLeft.SetParameters(&bqbase, &bqmain, prev_cutoff, prev_res, pEngine->SampleRate); - FilterRight.SetParameters(&bqbase, &bqmain, prev_cutoff, prev_res, pEngine->SampleRate); + FilterLeft.SetParameters( &bqbase, &bqmain, prev_cutoff + FILTER_CUTOFF_MIN, prev_res, pEngine->SampleRate); + FilterRight.SetParameters(&bqbase, &bqmain, prev_cutoff + FILTER_CUTOFF_MIN, prev_res, pEngine->SampleRate); } }