--- linuxsampler/trunk/src/engines/common/AbstractVoice.cpp 2011/12/11 20:50:31 2299 +++ linuxsampler/trunk/src/engines/common/AbstractVoice.cpp 2017/04/21 13:33:03 3118 @@ -4,7 +4,8 @@ * * * Copyright (C) 2003,2004 by Benno Senoner and Christian Schoenebeck * * Copyright (C) 2005-2008 Christian Schoenebeck * - * Copyright (C) 2009-2011 Christian Schoenebeck and Grigor Iliev * + * Copyright (C) 2009-2012 Christian Schoenebeck and Grigor Iliev * + * Copyright (C) 2013-2016 Christian Schoenebeck and Andreas Persson * * * * 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 * @@ -110,15 +111,12 @@ #endif // CONFIG_DEVMODE Type = VoiceType; - MIDIKey = itNoteOnEvent->Param.Note.Key; - MIDIVelocity = itNoteOnEvent->Param.Note.Velocity; - MIDIPan = pEngineChannel->ControllerTable[10]; - if (MIDIPan == 0 && pEngineChannel->GlobalPanRight == 1) MIDIPan = 64; // workaround used to determine whether the MIDI pan has not been set + pNote = pEngineChannel->pEngine->NoteByID( itNoteOnEvent->Param.Note.ID ); PlaybackState = playback_state_init; // mark voice as triggered, but no audio rendered yet Delay = itNoteOnEvent->FragmentPos(); itTriggerEvent = itNoteOnEvent; itKillEvent = Pool::Iterator(); - MidiKeyBase* pKeyInfo = GetMidiKeyInfo(MIDIKey); + MidiKeyBase* pKeyInfo = GetMidiKeyInfo(MIDIKey()); pGroupEvents = iKeyGroup ? pEngineChannel->ActiveKeyGroups[iKeyGroup] : 0; @@ -126,6 +124,8 @@ RgnInfo = GetRegionInfo(); InstrInfo = GetInstrumentInfo(); + MIDIPan = CalculatePan(pEngineChannel->iLastPanRequest); + AboutToTrigger(); // calculate volume @@ -141,14 +141,17 @@ // get starting crossfade volume level float crossfadeVolume = CalculateCrossfadeVolume(itNoteOnEvent->Param.Note.Velocity); - VolumeLeft = volume * pKeyInfo->PanLeft * AbstractEngine::PanCurve[64 - RgnInfo.Pan]; - VolumeRight = volume * pKeyInfo->PanRight * AbstractEngine::PanCurve[64 + RgnInfo.Pan]; + 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); - PanLeftSmoother.trigger(pEngineChannel->GlobalPanLeft, subfragmentRate); - PanRightSmoother.trigger(pEngineChannel->GlobalPanRight, 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; @@ -172,7 +175,7 @@ RAMLoop = (SmplInfo.HasLoops && (SmplInfo.LoopStart + SmplInfo.LoopLength) <= MaxRAMPos); if (OrderNewStream()) return -1; - dmsg(4,("Disk voice launched (cached samples: %d, total Samples: %d, MaxRAMPos: %d, RAMLooping: %s)\n", cachedsamples, SmplInfo.TotalFrameCount, MaxRAMPos, (RAMLoop) ? "yes" : "no")); + dmsg(4,("Disk voice launched (cached samples: %ld, total Samples: %d, MaxRAMPos: %lu, RAMLooping: %s)\n", cachedsamples, SmplInfo.TotalFrameCount, MaxRAMPos, (RAMLoop) ? "yes" : "no")); } else { // RAM only voice MaxRAMPos = cachedsamples; @@ -188,6 +191,9 @@ } 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); @@ -199,11 +205,29 @@ // 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(); } + const uint8_t pan = (pSignalUnitRack) ? pSignalUnitRack->GetEndpointUnit()->CalculatePan(MIDIPan) : MIDIPan; + NotePanLeft = (pNote) ? AbstractEngine::PanCurveValueNorm(pNote->Override.Pan, 0 /*left*/ ) : 1.f; + NotePanRight = (pNote) ? AbstractEngine::PanCurveValueNorm(pNote->Override.Pan, 1 /*right*/) : 1.f; + PanLeftSmoother.trigger( + AbstractEngine::PanCurve[128 - pan] * NotePanLeft, + 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, + 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 // setup initial volume in synthesis parameters #ifdef CONFIG_PROCESS_MUTED_CHANNELS @@ -221,8 +245,8 @@ finalVolume = pEngineChannel->MidiVolume * crossfadeVolume * pSignalUnitRack->GetEndpointUnit()->GetVolume(); } - finalSynthesisParameters.fFinalVolumeLeft = finalVolume * VolumeLeft * pEngineChannel->GlobalPanLeft; - finalSynthesisParameters.fFinalVolumeRight = finalVolume * VolumeRight * pEngineChannel->GlobalPanRight; + finalSynthesisParameters.fFinalVolumeLeft = finalVolume * VolumeLeft * PanLeftSmoother.render(); + finalSynthesisParameters.fFinalVolumeRight = finalVolume * VolumeRight * PanRightSmoother.render(); } #endif #endif @@ -245,7 +269,7 @@ // if portamento mode is on, we dedicate EG3 purely for portamento, otherwise if portamento is off we do as told by the patch bool bPortamento = pEngineChannel->PortamentoMode && pEngineChannel->PortamentoPos >= 0.0f; float eg3depth = (bPortamento) - ? RTMath::CentsToFreqRatio((pEngineChannel->PortamentoPos - (float) MIDIKey) * 100) + ? RTMath::CentsToFreqRatio((pEngineChannel->PortamentoPos - (float) MIDIKey()) * 100) : RTMath::CentsToFreqRatio(RgnInfo.EG3Depth); float eg3time = (bPortamento) ? pEngineChannel->PortamentoTime @@ -350,7 +374,7 @@ } AbstractEngineChannel* pChannel = pEngineChannel; - MidiKeyBase* pMidiKeyInfo = GetMidiKeyInfo(MIDIKey); + MidiKeyBase* pMidiKeyInfo = GetMidiKeyInfo(MIDIKey()); const bool bVoiceRequiresDedicatedRouting = pEngineChannel->GetFxSendCount() > 0 && @@ -376,10 +400,10 @@ RTList::Iterator itCCEvent = pChannel->pEvents->first(); RTList::Iterator itNoteEvent; - GetFirstEventOnKey(MIDIKey, itNoteEvent); + GetFirstEventOnKey(HostKey(), itNoteEvent); RTList::Iterator itGroupEvent; - if (pGroupEvents) itGroupEvent = pGroupEvents->first(); + if (pGroupEvents && !Orphan) itGroupEvent = pGroupEvents->first(); if (itTriggerEvent) { // skip events that happened before this voice was triggered while (itCCEvent && itCCEvent->FragmentPos() <= Skip) ++itCCEvent; @@ -396,7 +420,7 @@ } } - uint killPos; + uint killPos = 0; if (itKillEvent) { int maxFadeOutPos = Samples - GetEngine()->GetMinFadeOutSamples(); if (maxFadeOutPos < 0) { @@ -408,7 +432,7 @@ if (pSignalUnitRack == NULL) { pEG1->enterFadeOutStage(Samples / CONFIG_DEFAULT_SUBFRAGMENT_SIZE); } else { - // TODO: + pSignalUnitRack->EnterFadeOutStage(Samples / CONFIG_DEFAULT_SUBFRAGMENT_SIZE); } itKillEvent = Pool::Iterator(); } else { @@ -424,16 +448,17 @@ fFinalCutoff = VCFCutoffCtrl.fvalue; fFinalResonance = VCFResonanceCtrl.fvalue; - // process MIDI control change and pitchbend events for this subfragment + // process MIDI control change, aftertouch and pitchbend events for this subfragment processCCEvents(itCCEvent, iSubFragmentEnd); uint8_t pan = MIDIPan; - if (pSignalUnitRack != NULL) pan = pSignalUnitRack->GetEndpointUnit()->CaluclatePan(pan); - - PanLeftSmoother.update(AbstractEngine::PanCurve[128 - pan]); - PanRightSmoother.update(AbstractEngine::PanCurve[pan]); + if (pSignalUnitRack != NULL) pan = pSignalUnitRack->GetEndpointUnit()->CalculatePan(MIDIPan); - finalSynthesisParameters.fFinalPitch = Pitch.PitchBase * Pitch.PitchBend; - float fFinalVolume = VolumeSmoother.render() * CrossfadeSmoother.render(); + PanLeftSmoother.update(AbstractEngine::PanCurve[128 - pan] * NotePanLeft); + PanRightSmoother.update(AbstractEngine::PanCurve[pan] * NotePanRight); + + finalSynthesisParameters.fFinalPitch = Pitch.PitchBase * Pitch.PitchBend * NotePitch; + + float fFinalVolume = VolumeSmoother.render() * CrossfadeSmoother.render() * NoteVolumeSmoother.render(); #ifdef CONFIG_PROCESS_MUTED_CHANNELS if (pChannel->GetMute()) fFinalVolume = 0; #endif @@ -485,14 +510,18 @@ // process low frequency oscillators if (bLFO1Enabled) fFinalVolume *= (1.0f - pLFO1->render()); - if (bLFO2Enabled) fFinalCutoff *= pLFO2->render(); + if (bLFO2Enabled) fFinalCutoff *= (1.0f - pLFO2->render()); if (bLFO3Enabled) finalSynthesisParameters.fFinalPitch *= RTMath::CentsToFreqRatio(pLFO3->render()); } else { - // if the voice was killed in this subfragment, or if the - // filter EG is finished, switch EG1 to fade out stage - /*if ((itKillEvent && killPos <= iSubFragmentEnd) || - (SYNTHESIS_MODE_GET_FILTER(SynthesisMode) && - pEG2->getSegmentType() == EG::segment_end)) { + // if the voice was killed in this subfragment, enter fade out stage + if (itKillEvent && killPos <= iSubFragmentEnd) { + pSignalUnitRack->EnterFadeOutStage(); + itKillEvent = Pool::Iterator(); + } + + // if the filter EG is finished, switch EG1 to fade out stage + /*if (SYNTHESIS_MODE_GET_FILTER(SynthesisMode) && + pEG2->getSegmentType() == EG::segment_end) { pEG1->enterFadeOutStage(); itKillEvent = Pool::Iterator(); }*/ @@ -506,7 +535,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)); @@ -604,39 +636,48 @@ } /** - * Process given list of MIDI control change and pitch bend events for - * the given time. + * Process given list of MIDI control change, aftertouch 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 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); + pLFO1->updateByMIDICtrlValue(itEvent->Param.CC.Value); } if (itEvent->Param.CC.Controller == pLFO2->ExtController) { - pLFO2->update(itEvent->Param.CC.Value); + pLFO2->updateByMIDICtrlValue(itEvent->Param.CC.Value); } if (itEvent->Param.CC.Controller == pLFO3->ExtController) { - pLFO3->update(itEvent->Param.CC.Value); + pLFO3->updateByMIDICtrlValue(itEvent->Param.CC.Value); } } if (itEvent->Param.CC.Controller == 7) { // volume VolumeSmoother.update(AbstractEngine::VolumeCurve[itEvent->Param.CC.Value]); } else if (itEvent->Param.CC.Controller == 10) { // panpot - MIDIPan = itEvent->Param.CC.Value; + MIDIPan = CalculatePan(itEvent->Param.CC.Value); } } else if (itEvent->Type == Event::type_pitchbend) { // if pitch bend event processPitchEvent(itEvent); + } else if (itEvent->Type == Event::type_note_pressure) { + ProcessPolyphonicKeyPressureEvent(itEvent); } ProcessCCEvent(itEvent); @@ -661,8 +702,8 @@ } /** - * Process given list of MIDI note on, note off and sustain pedal events - * for the given time. + * Process given list of MIDI note on, note off, sustain pedal events and + * note synthesis parameter 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 @@ -671,9 +712,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); @@ -682,6 +723,52 @@ } } } + // 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) + { + switch (itEvent->Param.NoteSynthParam.Type) { + case Event::synth_param_volume: + NoteVolumeSmoother.update(itEvent->Param.NoteSynthParam.AbsValue); + break; + case Event::synth_param_pitch: + NotePitch = itEvent->Param.NoteSynthParam.AbsValue; + break; + case Event::synth_param_pan: + 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_amp_lfo_depth: + pLFO1->setScriptDepthFactor(itEvent->Param.NoteSynthParam.AbsValue); + break; + case Event::synth_param_amp_lfo_freq: + pLFO1->setScriptFrequencyFactor(itEvent->Param.NoteSynthParam.AbsValue, GetEngine()->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE); + break; + case Event::synth_param_pitch_lfo_depth: + pLFO3->setScriptDepthFactor(itEvent->Param.NoteSynthParam.AbsValue); + break; + case Event::synth_param_pitch_lfo_freq: + pLFO3->setScriptFrequencyFactor(itEvent->Param.NoteSynthParam.AbsValue, GetEngine()->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE); + break; + + case Event::synth_param_attack: + case Event::synth_param_decay: + case Event::synth_param_release: + break; // noop + } + } } } @@ -708,7 +795,7 @@ void AbstractVoice::UpdatePortamentoPos(Pool::Iterator& itNoteOffEvent) { if (pSignalUnitRack == NULL) { const float fFinalEG3Level = EG3.level(itNoteOffEvent->FragmentPos()); - pEngineChannel->PortamentoPos = (float) MIDIKey + RTMath::FreqRatioToCents(fFinalEG3Level) * 0.01f; + pEngineChannel->PortamentoPos = (float) MIDIKey() + RTMath::FreqRatioToCents(fFinalEG3Level) * 0.01f; } else { // TODO: } @@ -735,12 +822,12 @@ Voice::PitchInfo AbstractVoice::CalculatePitchInfo(int PitchBend) { PitchInfo pitch; - double pitchbasecents = InstrInfo.FineTune + RgnInfo.FineTune + GetEngine()->ScaleTuning[MIDIKey % 12]; + double pitchbasecents = InstrInfo.FineTune + RgnInfo.FineTune + GetEngine()->ScaleTuning[MIDIKey() % 12]; // GSt behaviour: maximum transpose up is 40 semitones. If // MIDI key is more than 40 semitones above unity note, // the transpose is not done. - if (!SmplInfo.Unpitched && (MIDIKey - (int) RgnInfo.UnityNote) < 40) pitchbasecents += (MIDIKey - (int) RgnInfo.UnityNote) * 100; + if (!SmplInfo.Unpitched && (MIDIKey() - (int) RgnInfo.UnityNote) < 40) pitchbasecents += (MIDIKey() - (int) RgnInfo.UnityNote) * 100; pitch.PitchBase = RTMath::CentsToFreqRatioUnlimited(pitchbasecents) * (double(SmplInfo.SampleRate) / double(GetEngine()->SampleRate)); pitch.PitchBendRange = 1.0 / 8192.0 * 100.0 * InstrInfo.PitchbendRange; @@ -748,6 +835,19 @@ return pitch; } + + void AbstractVoice::onScaleTuningChanged() { + PitchInfo pitch = this->Pitch; + double pitchbasecents = InstrInfo.FineTune + RgnInfo.FineTune + GetEngine()->ScaleTuning[MIDIKey() % 12]; + + // GSt behaviour: maximum transpose up is 40 semitones. If + // MIDI key is more than 40 semitones above unity note, + // the transpose is not done. + if (!SmplInfo.Unpitched && (MIDIKey() - (int) RgnInfo.UnityNote) < 40) pitchbasecents += (MIDIKey() - (int) RgnInfo.UnityNote) * 100; + + pitch.PitchBase = RTMath::CentsToFreqRatioUnlimited(pitchbasecents) * (double(SmplInfo.SampleRate) / double(GetEngine()->SampleRate)); + this->Pitch = pitch; + } double AbstractVoice::CalculateVolume(double velocityAttenuation) { // For 16 bit samples, we downscale by 32768 to convert from @@ -760,7 +860,7 @@ // the volume of release triggered samples depends on note length if (Type & Voice::type_release_trigger) { float noteLength = float(GetEngine()->FrameTime + Delay - - GetNoteOnTime(MIDIKey) ) / GetEngine()->SampleRate; + GetNoteOnTime(MIDIKey()) ) / GetEngine()->SampleRate; volume *= GetReleaseTriggerAttenuation(noteLength); }