--- linuxsampler/trunk/src/engines/common/AbstractVoice.cpp 2016/07/09 14:38:33 2931 +++ linuxsampler/trunk/src/engines/common/AbstractVoice.cpp 2016/10/31 00:05:00 3034 @@ -143,10 +143,14 @@ VolumeLeft = volume * pKeyInfo->PanLeft; VolumeRight = volume * pKeyInfo->PanRight; - float subfragmentRate = GetEngine()->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE; + // this rate is used for rather mellow volume fades + const float subfragmentRate = GetEngine()->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE; + // this rate is used for very fast volume fades + const float quickRampRate = RTMath::Min(subfragmentRate, GetEngine()->SampleRate * 0.001f /* 1ms */); CrossfadeSmoother.trigger(crossfadeVolume, subfragmentRate); + VolumeSmoother.trigger(pEngineChannel->MidiVolume, subfragmentRate); - NoteVolumeSmoother.trigger(pNote ? pNote->Override.Volume : 1.f, subfragmentRate); + NoteVolumeSmoother.trigger(pNote ? pNote->Override.Volume : 1.f, quickRampRate); // Check if the sample needs disk streaming or is too short for that long cachedsamples = GetSampleCacheSize() / SmplInfo.FrameSize; @@ -187,6 +191,8 @@ Pitch = CalculatePitchInfo(PitchBend); NotePitch = (pNote) ? pNote->Override.Pitch : 1.0f; + NoteCutoff = (pNote) ? pNote->Override.Cutoff : 1.0f; + NoteResonance = (pNote) ? pNote->Override.Resonance : 1.0f; // the length of the decay and release curves are dependent on the velocity const double velrelease = 1 / GetVelocityRelease(itNoteOnEvent->Param.Note.Velocity); @@ -198,6 +204,12 @@ // calculate influence of EG1 controller on EG1's parameters EGInfo egInfo = CalculateEG1ControllerInfluence(eg1controllervalue); + if (pNote) { + egInfo.Attack *= pNote->Override.Attack; + egInfo.Decay *= pNote->Override.Decay; + egInfo.Release *= pNote->Override.Release; + } + TriggerEG1(egInfo, velrelease, velocityAttenuation, GetEngine()->SampleRate, itNoteOnEvent->Param.Note.Velocity); } else { pSignalUnitRack->Trigger(); @@ -208,11 +220,11 @@ NotePanRight = (pNote) ? AbstractEngine::PanCurveValueNorm(pNote->Override.Pan, 1 /*right*/) : 1.f; PanLeftSmoother.trigger( AbstractEngine::PanCurve[128 - pan] * NotePanLeft, - subfragmentRate + quickRampRate //NOTE: maybe we should have 2 separate pan smoothers, one for MIDI CC10 (with slow rate) and one for instrument script change_pan() calls (with fast rate) ); PanRightSmoother.trigger( AbstractEngine::PanCurve[pan] * NotePanRight, - subfragmentRate + quickRampRate //NOTE: maybe we should have 2 separate pan smoothers, one for MIDI CC10 (with slow rate) and one for instrument script change_pan() calls (with fast rate) ); #ifdef CONFIG_INTERPOLATE_VOLUME @@ -522,7 +534,10 @@ pSignalUnitRack->GetEndpointUnit()->CalculatePitch(finalSynthesisParameters.fFinalPitch); } - + + fFinalCutoff *= NoteCutoff; + fFinalResonance *= NoteResonance; + // limit the pitch so we don't read outside the buffer finalSynthesisParameters.fFinalPitch = RTMath::Min(finalSynthesisParameters.fFinalPitch, float(1 << CONFIG_MAX_PITCH)); @@ -628,13 +643,20 @@ */ void AbstractVoice::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->Type == Event::type_control_change || itEvent->Type == Event::type_channel_pressure) + && 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 (itEvent->Param.CC.Controller == CTRL_TABLE_IDX_AFTERTOUCH || + itEvent->Type == Event::type_channel_pressure) + { + ProcessChannelPressureEvent(itEvent); + } if (pSignalUnitRack == NULL) { if (itEvent->Param.CC.Controller == pLFO1->ExtController) { pLFO1->update(itEvent->Param.CC.Value); @@ -653,8 +675,6 @@ } } else if (itEvent->Type == Event::type_pitchbend) { // if pitch bend event processPitchEvent(itEvent); - } else if (itEvent->Type == Event::type_channel_pressure) { - ProcessChannelPressureEvent(itEvent); } else if (itEvent->Type == Event::type_note_pressure) { ProcessPolyphonicKeyPressureEvent(itEvent); } @@ -691,9 +711,9 @@ for (; itEvent && itEvent->FragmentPos() <= End; ++itEvent) { // some voice types ignore note off if (!(Type & (Voice::type_one_shot | Voice::type_release_trigger | Voice::type_controller_triggered))) { - if (itEvent->Type == Event::type_release) { + if (itEvent->Type == Event::type_release_key) { EnterReleaseStage(); - } else if (itEvent->Type == Event::type_cancel_release) { + } else if (itEvent->Type == Event::type_cancel_release_key) { if (pSignalUnitRack == NULL) { pEG1->update(EG::event_cancel_release, GetEngine()->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE); pEG2->update(EG::event_cancel_release, GetEngine()->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE); @@ -702,6 +722,12 @@ } } } + // process stop-note events (caused by built-in instrument script function note_off()) + if (itEvent->Type == Event::type_release_note && pNote && + pEngineChannel->pEngine->NoteByID( itEvent->Param.Note.ID ) == pNote) + { + EnterReleaseStage(); + } // process synthesis parameter events (caused by built-in realt-time instrument script functions) if (itEvent->Type == Event::type_note_synth_param && pNote && pEngineChannel->pEngine->NoteByID( itEvent->Param.NoteSynthParam.NoteID ) == pNote) @@ -717,6 +743,17 @@ NotePanLeft = AbstractEngine::PanCurveValueNorm(itEvent->Param.NoteSynthParam.AbsValue, 0 /*left*/); NotePanRight = AbstractEngine::PanCurveValueNorm(itEvent->Param.NoteSynthParam.AbsValue, 1 /*right*/); break; + case Event::synth_param_cutoff: + NoteCutoff = itEvent->Param.NoteSynthParam.AbsValue; + break; + case Event::synth_param_resonance: + NoteResonance = itEvent->Param.NoteSynthParam.AbsValue; + break; + + case Event::synth_param_attack: + case Event::synth_param_decay: + case Event::synth_param_release: + break; // noop } } }