--- linuxsampler/trunk/src/engines/gig/Voice.cpp 2004/08/22 14:46:47 225 +++ linuxsampler/trunk/src/engines/gig/Voice.cpp 2004/09/19 23:44:23 247 @@ -27,8 +27,6 @@ namespace LinuxSampler { namespace gig { - // FIXME: no support for layers (nor crossfades) yet - const float Voice::FILTER_CUTOFF_COEFF(CalculateFilterCutoffCoeff()); const int Voice::FILTER_UPDATE_MASK(CalculateFilterUpdateMask()); @@ -57,6 +55,7 @@ pLFO1 = NULL; pLFO2 = NULL; pLFO3 = NULL; + KeyGroup = 0; } Voice::~Voice() { @@ -104,47 +103,166 @@ * Initializes and triggers the voice, a disk stream will be launched if * needed. * - * @param pNoteOnEvent - 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 - * @returns 0 on success, a value < 0 if something failed + * @param pNoteOnEvent - 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) + * @returns 0 on success, a value < 0 if something failed */ - int Voice::Trigger(Event* pNoteOnEvent, int PitchBend, ::gig::Instrument* pInstrument) { + int Voice::Trigger(Event* pNoteOnEvent, int PitchBend, ::gig::Instrument* pInstrument, int iLayer, bool ReleaseTriggerVoice) { if (!pInstrument) { dmsg(1,("voice::trigger: !pInstrument\n")); exit(EXIT_FAILURE); } + Type = type_normal; Active = true; - MIDIKey = pNoteOnEvent->Key; + MIDIKey = pNoteOnEvent->Param.Note.Key; pRegion = pInstrument->GetRegion(MIDIKey); PlaybackState = playback_state_ram; // we always start playback from RAM cache and switch then to disk if needed - Pos = 0; Delay = pNoteOnEvent->FragmentPos(); pTriggerEvent = pNoteOnEvent; + pKillEvent = NULL; if (!pRegion) { - std::cerr << "Audio Thread: No Region defined for MIDI key " << MIDIKey << std::endl << std::flush; - Kill(); + std::cerr << "gig::Voice: No Region defined for MIDI key " << MIDIKey << std::endl << std::flush; + KillImmediately(); return -1; } - //TODO: current MIDI controller values are not taken into account yet - ::gig::DimensionRegion* pDimRgn = NULL; - for (int i = pRegion->Dimensions - 1; i >= 0; i--) { // Check if instrument has a velocity split - if (pRegion->pDimensionDefinitions[i].dimension == ::gig::dimension_velocity) { - uint DimValues[5] = {0,0,0,0,0}; - DimValues[i] = pNoteOnEvent->Velocity; - pDimRgn = pRegion->GetDimensionRegionByValue(DimValues[4],DimValues[3],DimValues[2],DimValues[1],DimValues[0]); - break; + KeyGroup = pRegion->KeyGroup; + + // 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}; + 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; + // 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++) + pEngine->LaunchVoice(pNoteOnEvent, iNewLayer, ReleaseTriggerVoice); + break; + case ::gig::dimension_velocity: + DimValues[i] = pNoteOnEvent->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) pNoteOnEvent->Param.Note.Key; + break; + case ::gig::dimension_modwheel: + DimValues[i] = pEngine->ControllerTable[1]; + break; + case ::gig::dimension_breath: + DimValues[i] = pEngine->ControllerTable[2]; + break; + case ::gig::dimension_foot: + DimValues[i] = pEngine->ControllerTable[4]; + break; + case ::gig::dimension_portamentotime: + DimValues[i] = pEngine->ControllerTable[5]; + break; + case ::gig::dimension_effect1: + DimValues[i] = pEngine->ControllerTable[12]; + break; + case ::gig::dimension_effect2: + DimValues[i] = pEngine->ControllerTable[13]; + break; + case ::gig::dimension_genpurpose1: + DimValues[i] = pEngine->ControllerTable[16]; + break; + case ::gig::dimension_genpurpose2: + DimValues[i] = pEngine->ControllerTable[17]; + break; + case ::gig::dimension_genpurpose3: + DimValues[i] = pEngine->ControllerTable[18]; + break; + case ::gig::dimension_genpurpose4: + DimValues[i] = pEngine->ControllerTable[19]; + break; + case ::gig::dimension_sustainpedal: + DimValues[i] = pEngine->ControllerTable[64]; + break; + case ::gig::dimension_portamento: + DimValues[i] = pEngine->ControllerTable[65]; + break; + case ::gig::dimension_sostenutopedal: + DimValues[i] = pEngine->ControllerTable[66]; + break; + case ::gig::dimension_softpedal: + DimValues[i] = pEngine->ControllerTable[67]; + break; + case ::gig::dimension_genpurpose5: + DimValues[i] = pEngine->ControllerTable[80]; + break; + case ::gig::dimension_genpurpose6: + DimValues[i] = pEngine->ControllerTable[81]; + break; + case ::gig::dimension_genpurpose7: + DimValues[i] = pEngine->ControllerTable[82]; + break; + case ::gig::dimension_genpurpose8: + DimValues[i] = pEngine->ControllerTable[83]; + break; + case ::gig::dimension_effect1depth: + DimValues[i] = pEngine->ControllerTable[91]; + break; + case ::gig::dimension_effect2depth: + DimValues[i] = pEngine->ControllerTable[92]; + break; + case ::gig::dimension_effect3depth: + DimValues[i] = pEngine->ControllerTable[93]; + break; + case ::gig::dimension_effect4depth: + DimValues[i] = pEngine->ControllerTable[94]; + break; + case ::gig::dimension_effect5depth: + DimValues[i] = pEngine->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; } } - if (!pDimRgn) { // if there was no velocity split - pDimRgn = pRegion->GetDimensionRegionByValue(0,0,0,0,0); + pDimRgn = pRegion->GetDimensionRegionByValue(DimValues[4],DimValues[3],DimValues[2],DimValues[1],DimValues[0]); + + // get starting crossfade volume level + switch (pDimRgn->AttenuationController.type) { + case ::gig::attenuation_ctrl_t::type_channelaftertouch: + CrossfadeVolume = 1.0f; //TODO: aftertouch not supported yet + break; + case ::gig::attenuation_ctrl_t::type_velocity: + CrossfadeVolume = CrossfadeAttenuation(pNoteOnEvent->Param.Note.Velocity); + break; + case ::gig::attenuation_ctrl_t::type_controlchange: //FIXME: currently not sample accurate + CrossfadeVolume = CrossfadeAttenuation(pEngine->ControllerTable[pDimRgn->AttenuationController.controller_number]); + break; + case ::gig::attenuation_ctrl_t::type_none: // no crossfade defined + default: + CrossfadeVolume = 1.0f; } + const float fpan = float(RTMath::Max(RTMath::Min(pDimRgn->Pan, 63), -64)) / 64.0f; + PanLeft = 1.0f - fpan; + PanRight = 1.0f + fpan; + pSample = pDimRgn->pSample; // sample won't change until the voice is finished + Pos = pDimRgn->SampleStartOffset; // offset where we should start playback of sample (0 - 2000 sample points) + // Check if the sample needs disk streaming or is too short for that long cachedsamples = pSample->GetCache().Size / pSample->FrameSize; DiskVoice = cachedsamples < pSample->SamplesTotal; @@ -161,7 +279,7 @@ if (pDiskThread->OrderNewStream(&DiskStreamRef, pSample, MaxRAMPos, !RAMLoop) < 0) { dmsg(1,("Disk stream order failed!\n")); - Kill(); + KillImmediately(); return -1; } dmsg(4,("Disk voice launched (cached samples: %d, total Samples: %d, MaxRAMPos: %d, RAMLooping: %s)\n", cachedsamples, pSample->SamplesTotal, MaxRAMPos, (RAMLoop) ? "yes" : "no")); @@ -179,14 +297,14 @@ // calculate initial pitch value { - double pitchbasecents = pDimRgn->FineTune * 10; + double pitchbasecents = pDimRgn->FineTune * 10 + (int) pEngine->ScaleTuning[MIDIKey % 12]; if (pDimRgn->PitchTrack) pitchbasecents += (MIDIKey - (int) pDimRgn->UnityNote) * 100; - this->PitchBase = RTMath::CentsToFreqRatio(pitchbasecents); + 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 } - Volume = pDimRgn->GetVelocityAttenuation(pNoteOnEvent->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->GetVelocityAttenuation(pNoteOnEvent->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) // setup EG 1 (VCA EG) @@ -201,7 +319,7 @@ eg1controllervalue = 0; // TODO: aftertouch not yet supported break; case ::gig::eg1_ctrl_t::type_velocity: - eg1controllervalue = pNoteOnEvent->Velocity; + eg1controllervalue = pNoteOnEvent->Param.Note.Velocity; break; case ::gig::eg1_ctrl_t::type_controlchange: // MIDI control change controller eg1controllervalue = pEngine->ControllerTable[pDimRgn->EG1Controller.controller_number]; @@ -240,7 +358,7 @@ eg2controllervalue = 0; // TODO: aftertouch not yet supported break; case ::gig::eg2_ctrl_t::type_velocity: - eg2controllervalue = pNoteOnEvent->Velocity; + eg2controllervalue = pNoteOnEvent->Param.Note.Velocity; break; case ::gig::eg2_ctrl_t::type_controlchange: // MIDI control change controller eg2controllervalue = pEngine->ControllerTable[pDimRgn->EG2Controller.controller_number]; @@ -468,13 +586,13 @@ // calculate cutoff frequency float cutoff = (!VCFCutoffCtrl.controller) - ? exp((float) (127 - pNoteOnEvent->Velocity) * (float) pDimRgn->VCFVelocityScale * 6.2E-5f * FILTER_CUTOFF_COEFF) * FILTER_CUTOFF_MAX + ? exp((float) (127 - pNoteOnEvent->Param.Note.Velocity) * (float) pDimRgn->VCFVelocityScale * 6.2E-5f * FILTER_CUTOFF_COEFF) * FILTER_CUTOFF_MAX : exp((float) VCFCutoffCtrl.value * 0.00787402f * FILTER_CUTOFF_COEFF) * FILTER_CUTOFF_MAX; // calculate resonance float resonance = (float) VCFResonanceCtrl.value * 0.00787f; // 0.0..1.0 if (pDimRgn->VCFKeyboardTracking) { - resonance += (float) (pNoteOnEvent->Key - pDimRgn->VCFKeyboardTrackingBreakpoint) * 0.00787f; + resonance += (float) (pNoteOnEvent->Param.Note.Key - pDimRgn->VCFKeyboardTrackingBreakpoint) * 0.00787f; } Constrain(resonance, 0.0, 1.0); // correct resonance if outside allowed value range (0.0..1.0) @@ -492,10 +610,6 @@ } #endif // ENABLE_FILTER - // ************************************************ - // TODO: ARTICULATION DATA HANDLING IS MISSING HERE - // ************************************************ - return 0; // success } @@ -513,7 +627,7 @@ void Voice::Render(uint Samples) { // Reset the synthesis parameter matrix - pEngine->ResetSynthesisParameters(Event::destination_vca, this->Volume * pEngine->GlobalVolume); + pEngine->ResetSynthesisParameters(Event::destination_vca, this->Volume * this->CrossfadeVolume * pEngine->GlobalVolume); pEngine->ResetSynthesisParameters(Event::destination_vco, this->PitchBase); #if ENABLE_FILTER pEngine->ResetSynthesisParameters(Event::destination_vcfc, VCFCutoffCtrl.fvalue); @@ -526,7 +640,7 @@ // Let all modulators write their parameter changes to the synthesis parameter matrix for the current audio fragment - pEG1->Process(Samples, pEngine->pMIDIKeyInfo[MIDIKey].pEvents, pTriggerEvent, this->Pos, this->PitchBase * this->PitchBend); + pEG1->Process(Samples, pEngine->pMIDIKeyInfo[MIDIKey].pEvents, pTriggerEvent, this->Pos, this->PitchBase * this->PitchBend, pKillEvent); #if ENABLE_FILTER pEG2->Process(Samples, pEngine->pMIDIKeyInfo[MIDIKey].pEvents, pTriggerEvent, this->Pos, this->PitchBase * this->PitchBend); #endif // ENABLE_FILTER @@ -547,7 +661,7 @@ case playback_state_ram: { if (RAMLoop) InterpolateAndLoop(Samples, (sample_t*) pSample->GetCache().pStart, Delay); - else Interpolate(Samples, (sample_t*) pSample->GetCache().pStart, Delay); + else InterpolateNoLoop(Samples, (sample_t*) pSample->GetCache().pStart, Delay); if (DiskVoice) { // check if we reached the allowed limit of the sample RAM cache if (Pos > MaxRAMPos) { @@ -567,7 +681,7 @@ DiskStreamRef.pStream = pDiskThread->AskForCreatedStream(DiskStreamRef.OrderID); if (!DiskStreamRef.pStream) { std::cout << stderr << "Disk stream not available in time!" << std::endl << std::flush; - Kill(); + KillImmediately(); return; } DiskStreamRef.pStream->IncrementReadPos(pSample->Channels * (RTMath::DoubleToInt(Pos) - MaxRAMPos)); @@ -581,20 +695,21 @@ } sample_t* ptr = DiskStreamRef.pStream->GetReadPtr(); // get the current read_ptr within the ringbuffer where we read the samples from - Interpolate(Samples, ptr, Delay); + InterpolateNoLoop(Samples, ptr, Delay); DiskStreamRef.pStream->IncrementReadPos(RTMath::DoubleToInt(Pos) * pSample->Channels); Pos -= RTMath::DoubleToInt(Pos); } break; case playback_state_end: - Kill(); // free voice + KillImmediately(); // free voice break; } - #if ENABLE_FILTER // Reset synthesis event lists (except VCO, as VCO events apply channel wide currently) + pEngine->pSynthesisEvents[Event::destination_vca]->clear(); + #if ENABLE_FILTER pEngine->pSynthesisEvents[Event::destination_vcfc]->clear(); pEngine->pSynthesisEvents[Event::destination_vcfr]->clear(); #endif // ENABLE_FILTER @@ -638,26 +753,30 @@ while (pCCEvent && pCCEvent->FragmentPos() <= Delay) pCCEvent = pEngine->pCCEvents->next(); } while (pCCEvent) { - if (pCCEvent->Controller) { // if valid MIDI controller + if (pCCEvent->Param.CC.Controller) { // if valid MIDI controller #if ENABLE_FILTER - if (pCCEvent->Controller == VCFCutoffCtrl.controller) { + if (pCCEvent->Param.CC.Controller == VCFCutoffCtrl.controller) { pEngine->pSynthesisEvents[Event::destination_vcfc]->alloc_assign(*pCCEvent); } - if (pCCEvent->Controller == VCFResonanceCtrl.controller) { + if (pCCEvent->Param.CC.Controller == VCFResonanceCtrl.controller) { pEngine->pSynthesisEvents[Event::destination_vcfr]->alloc_assign(*pCCEvent); } #endif // ENABLE_FILTER - if (pCCEvent->Controller == pLFO1->ExtController) { + if (pCCEvent->Param.CC.Controller == pLFO1->ExtController) { pLFO1->SendEvent(pCCEvent); } #if ENABLE_FILTER - if (pCCEvent->Controller == pLFO2->ExtController) { + if (pCCEvent->Param.CC.Controller == pLFO2->ExtController) { pLFO2->SendEvent(pCCEvent); } #endif // ENABLE_FILTER - if (pCCEvent->Controller == pLFO3->ExtController) { + if (pCCEvent->Param.CC.Controller == pLFO3->ExtController) { pLFO3->SendEvent(pCCEvent); } + if (pDimRgn->AttenuationController.type == ::gig::attenuation_ctrl_t::type_controlchange && + pCCEvent->Param.CC.Controller == pDimRgn->AttenuationController.controller_number) { // if crossfade event + pEngine->pSynthesisEvents[Event::destination_vca]->alloc_assign(*pCCEvent); + } } pCCEvent = pEngine->pCCEvents->next(); @@ -685,7 +804,7 @@ // calculate the influence length of this event (in sample points) uint end = (pNextVCOEvent) ? pNextVCOEvent->FragmentPos() : Samples; - pitch = RTMath::CentsToFreqRatio(((double) pVCOEvent->Pitch / 8192.0) * 200.0); // +-two semitones = +-200 cents + pitch = RTMath::CentsToFreqRatio(((double) pVCOEvent->Param.Pitch.Pitch / 8192.0) * 200.0); // +-two semitones = +-200 cents // apply pitch value to the pitch parameter sequence for (uint i = pVCOEvent->FragmentPos(); i < end; i++) { @@ -697,6 +816,33 @@ if (pVCOEventList->last()) this->PitchBend = pitch; } + // process volume / attenuation events (TODO: we only handle and _expect_ crossfade events here ATM !) + { + RTEList* pVCAEventList = pEngine->pSynthesisEvents[Event::destination_vca]; + Event* pVCAEvent = pVCAEventList->first(); + if (Delay) { // skip events that happened before this voice was triggered + while (pVCAEvent && pVCAEvent->FragmentPos() <= Delay) pVCAEvent = pVCAEventList->next(); + } + float crossfadevolume; + while (pVCAEvent) { + Event* pNextVCAEvent = pVCAEventList->next(); + + // calculate the influence length of this event (in sample points) + uint end = (pNextVCAEvent) ? pNextVCAEvent->FragmentPos() : Samples; + + crossfadevolume = CrossfadeAttenuation(pVCAEvent->Param.CC.Value); + + float effective_volume = crossfadevolume * this->Volume * pEngine->GlobalVolume; + + // apply volume value to the volume parameter sequence + for (uint i = pVCAEvent->FragmentPos(); i < end; i++) { + pEngine->pSynthesisParameters[Event::destination_vca][i] = effective_volume; + } + + pVCAEvent = pNextVCAEvent; + } + if (pVCAEventList->last()) this->CrossfadeVolume = crossfadevolume; + } #if ENABLE_FILTER // process filter cutoff events @@ -713,7 +859,7 @@ // calculate the influence length of this event (in sample points) uint end = (pNextCutoffEvent) ? pNextCutoffEvent->FragmentPos() : Samples; - cutoff = exp((float) pCutoffEvent->Value * 0.00787402f * FILTER_CUTOFF_COEFF) * FILTER_CUTOFF_MAX - FILTER_CUTOFF_MIN; + cutoff = exp((float) pCutoffEvent->Param.CC.Value * 0.00787402f * FILTER_CUTOFF_COEFF) * FILTER_CUTOFF_MAX - FILTER_CUTOFF_MIN; // apply cutoff frequency to the cutoff parameter sequence for (uint i = pCutoffEvent->FragmentPos(); i < end; i++) { @@ -739,8 +885,8 @@ uint end = (pNextResonanceEvent) ? pNextResonanceEvent->FragmentPos() : Samples; // convert absolute controller value to differential - int ctrldelta = pResonanceEvent->Value - VCFResonanceCtrl.value; - VCFResonanceCtrl.value = pResonanceEvent->Value; + int ctrldelta = pResonanceEvent->Param.CC.Value - VCFResonanceCtrl.value; + VCFResonanceCtrl.value = pResonanceEvent->Param.CC.Value; float resonancedelta = (float) ctrldelta * 0.00787f; // 0.0..1.0 @@ -751,7 +897,7 @@ pResonanceEvent = pNextResonanceEvent; } - if (pResonanceEventList->last()) VCFResonanceCtrl.fvalue = pResonanceEventList->last()->Value * 0.00787f; // needed for initialization of parameter matrix next time + if (pResonanceEventList->last()) VCFResonanceCtrl.fvalue = pResonanceEventList->last()->Param.CC.Value * 0.00787f; // needed for initialization of parameter matrix next time } #endif // ENABLE_FILTER } @@ -803,34 +949,22 @@ #endif // ENABLE_FILTER /** - * Interpolates the input audio data (no loop). + * Interpolates the input audio data (without looping). * * @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::Interpolate(uint Samples, sample_t* pSrc, uint Skip) { + void Voice::InterpolateNoLoop(uint Samples, sample_t* pSrc, uint Skip) { int i = Skip; // FIXME: assuming either mono or stereo if (this->pSample->Channels == 2) { // Stereo Sample - while (i < Samples) { - InterpolateOneStep_Stereo(pSrc, i, - pEngine->pSynthesisParameters[Event::destination_vca][i], - pEngine->pSynthesisParameters[Event::destination_vco][i], - pEngine->pBasicFilterParameters[i], - pEngine->pMainFilterParameters[i]); - } + while (i < Samples) InterpolateStereo(pSrc, i); } else { // Mono Sample - while (i < Samples) { - InterpolateOneStep_Mono(pSrc, i, - pEngine->pSynthesisParameters[Event::destination_vca][i], - pEngine->pSynthesisParameters[Event::destination_vco][i], - pEngine->pBasicFilterParameters[i], - pEngine->pMainFilterParameters[i]); - } + while (i < Samples) InterpolateMono(pSrc, i); } } @@ -850,32 +984,18 @@ if (pSample->LoopPlayCount) { // render loop (loop count limited) while (i < Samples && LoopCyclesLeft) { - InterpolateOneStep_Stereo(pSrc, i, - pEngine->pSynthesisParameters[Event::destination_vca][i], - pEngine->pSynthesisParameters[Event::destination_vco][i], - pEngine->pBasicFilterParameters[i], - pEngine->pMainFilterParameters[i]); + InterpolateStereo(pSrc, i); if (Pos > pSample->LoopEnd) { Pos = pSample->LoopStart + fmod(Pos - pSample->LoopEnd, pSample->LoopSize);; LoopCyclesLeft--; } } // render on without loop - while (i < Samples) { - InterpolateOneStep_Stereo(pSrc, i, - pEngine->pSynthesisParameters[Event::destination_vca][i], - pEngine->pSynthesisParameters[Event::destination_vco][i], - pEngine->pBasicFilterParameters[i], - pEngine->pMainFilterParameters[i]); - } + while (i < Samples) InterpolateStereo(pSrc, i); } else { // render loop (endless loop) while (i < Samples) { - InterpolateOneStep_Stereo(pSrc, i, - pEngine->pSynthesisParameters[Event::destination_vca][i], - pEngine->pSynthesisParameters[Event::destination_vco][i], - pEngine->pBasicFilterParameters[i], - pEngine->pMainFilterParameters[i]); + InterpolateStereo(pSrc, i); if (Pos > pSample->LoopEnd) { Pos = pSample->LoopStart + fmod(Pos - pSample->LoopEnd, pSample->LoopSize); } @@ -886,32 +1006,18 @@ if (pSample->LoopPlayCount) { // render loop (loop count limited) while (i < Samples && LoopCyclesLeft) { - InterpolateOneStep_Mono(pSrc, i, - pEngine->pSynthesisParameters[Event::destination_vca][i], - pEngine->pSynthesisParameters[Event::destination_vco][i], - pEngine->pBasicFilterParameters[i], - pEngine->pMainFilterParameters[i]); + InterpolateMono(pSrc, i); if (Pos > pSample->LoopEnd) { Pos = pSample->LoopStart + fmod(Pos - pSample->LoopEnd, pSample->LoopSize);; LoopCyclesLeft--; } } // render on without loop - while (i < Samples) { - InterpolateOneStep_Mono(pSrc, i, - pEngine->pSynthesisParameters[Event::destination_vca][i], - pEngine->pSynthesisParameters[Event::destination_vco][i], - pEngine->pBasicFilterParameters[i], - pEngine->pMainFilterParameters[i]); - } + while (i < Samples) InterpolateMono(pSrc, i); } else { // render loop (endless loop) while (i < Samples) { - InterpolateOneStep_Mono(pSrc, i, - pEngine->pSynthesisParameters[Event::destination_vca][i], - pEngine->pSynthesisParameters[Event::destination_vco][i], - pEngine->pBasicFilterParameters[i], - pEngine->pMainFilterParameters[i]); + InterpolateMono(pSrc, i); if (Pos > pSample->LoopEnd) { Pos = pSample->LoopStart + fmod(Pos - pSample->LoopEnd, pSample->LoopSize);; } @@ -921,13 +1027,32 @@ } /** - * Immediately kill the voice. + * 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 + * until the kill event actually occured! + * + * @see Kill() */ - void Voice::Kill() { + void Voice::KillImmediately() { if (DiskVoice && DiskStreamRef.State != Stream::state_unused) { pDiskThread->OrderDeletionOfStream(&DiskStreamRef); } Reset(); } + /** + * Kill the voice in regular sense. Let the voice render audio until + * the kill event actually occured and then fade down the volume level + * very quickly and let the voice die finally. Unlike a normal release + * of a voice, a kill process cannot be cancalled and is therefore + * usually used for voice stealing and key group conflicts. + * + * @param pKillEvent - event which caused the voice to be killed + */ + void Voice::Kill(Event* pKillEvent) { + if (pTriggerEvent && pKillEvent->FragmentPos() <= pTriggerEvent->FragmentPos()) return; + this->pKillEvent = pKillEvent; + } + }} // namespace LinuxSampler::gig