--- linuxsampler/trunk/src/engines/common/AbstractVoice.cpp 2011/07/28 12:35:49 2219 +++ linuxsampler/trunk/src/engines/common/AbstractVoice.cpp 2016/07/17 18:41:21 2963 @@ -4,7 +4,7 @@ * * * 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-2015 Christian Schoenebeck and Grigor Iliev * * * * 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 * @@ -43,12 +43,24 @@ finalSynthesisParameters.filterLeft.Reset(); finalSynthesisParameters.filterRight.Reset(); + + pEq = NULL; + bEqSupport = false; } AbstractVoice::~AbstractVoice() { if (pLFO1) delete pLFO1; if (pLFO2) delete pLFO2; if (pLFO3) delete pLFO3; + + if(pEq != NULL) delete pEq; + } + + void AbstractVoice::CreateEq() { + if(!bEqSupport) return; + if(pEq != NULL) delete pEq; + pEq = new EqSupport; + pEq->InitEffect(GetEngine()->pAudioOutputDevice); } /** @@ -98,15 +110,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; @@ -114,6 +123,8 @@ RgnInfo = GetRegionInfo(); InstrInfo = GetInstrumentInfo(); + MIDIPan = CalculatePan(pEngineChannel->iLastPanRequest); + AboutToTrigger(); // calculate volume @@ -129,14 +140,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; @@ -160,7 +174,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; @@ -176,6 +190,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); @@ -187,11 +204,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 @@ -209,8 +244,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 @@ -233,7 +268,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 @@ -294,6 +329,15 @@ VCFCutoffCtrl.controller = 0; VCFResonanceCtrl.controller = 0; } + + const bool bEq = + pSignalUnitRack != NULL && pSignalUnitRack->HasEq() && pEq->HasSupport(); + + if (bEq) { + pEq->GetInChannelLeft()->Clear(); + pEq->GetInChannelRight()->Clear(); + pEq->RenderAudio(GetEngine()->pAudioOutputDevice->MaxSamplesPerCycle()); + } return 0; // success } @@ -312,14 +356,39 @@ * @param Skip - number of sample points to skip in output buffer */ void AbstractVoice::Synthesize(uint Samples, sample_t* pSrc, uint Skip) { + bool delay = false; // Whether the voice playback should be delayed for this call + + if (pSignalUnitRack != NULL) { + uint delaySteps = pSignalUnitRack->GetEndpointUnit()->DelayTrigger(); + if (delaySteps > 0) { // delay on the endpoint unit means delay of the voice playback + if (delaySteps >= Samples) { + pSignalUnitRack->GetEndpointUnit()->DecreaseDelay(Samples); + delay = true; + } else { + pSignalUnitRack->GetEndpointUnit()->DecreaseDelay(delaySteps); + Samples -= delaySteps; + Skip += delaySteps; + } + } + } + AbstractEngineChannel* pChannel = pEngineChannel; - MidiKeyBase* pMidiKeyInfo = GetMidiKeyInfo(MIDIKey); + MidiKeyBase* pMidiKeyInfo = GetMidiKeyInfo(MIDIKey()); const bool bVoiceRequiresDedicatedRouting = pEngineChannel->GetFxSendCount() > 0 && (pMidiKeyInfo->ReverbSend || pMidiKeyInfo->ChorusSend); + + const bool bEq = + pSignalUnitRack != NULL && pSignalUnitRack->HasEq() && pEq->HasSupport(); - if (bVoiceRequiresDedicatedRouting) { + if (bEq) { + pEq->GetInChannelLeft()->Clear(); + pEq->GetInChannelRight()->Clear(); + finalSynthesisParameters.pOutLeft = &pEq->GetInChannelLeft()->Buffer()[Skip]; + finalSynthesisParameters.pOutRight = &pEq->GetInChannelRight()->Buffer()[Skip]; + pSignalUnitRack->UpdateEqSettings(pEq); + } else if (bVoiceRequiresDedicatedRouting) { finalSynthesisParameters.pOutLeft = &GetEngine()->pDedicatedVoiceChannelLeft->Buffer()[Skip]; finalSynthesisParameters.pOutRight = &GetEngine()->pDedicatedVoiceChannelRight->Buffer()[Skip]; } else { @@ -330,10 +399,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; @@ -362,7 +431,7 @@ if (pSignalUnitRack == NULL) { pEG1->enterFadeOutStage(Samples / CONFIG_DEFAULT_SUBFRAGMENT_SIZE); } else { - // TODO: + pSignalUnitRack->EnterFadeOutStage(Samples / CONFIG_DEFAULT_SUBFRAGMENT_SIZE); } itKillEvent = Pool::Iterator(); } else { @@ -378,16 +447,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 @@ -395,7 +465,7 @@ // process transition events (note on, note off & sustain pedal) processTransitionEvents(itNoteEvent, iSubFragmentEnd); processGroupEvents(itGroupEvent, iSubFragmentEnd); - + if (pSignalUnitRack == NULL) { // if the voice was killed in this subfragment, or if the // filter EG is finished, switch EG1 to fade out stage @@ -439,14 +509,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(); }*/ @@ -460,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)); @@ -493,7 +570,7 @@ fFinalVolume * VolumeRight * PanRightSmoother.render(); #endif // render audio for one subfragment - RunSynthesisFunction(SynthesisMode, &finalSynthesisParameters, &loop); + if (!delay) RunSynthesisFunction(SynthesisMode, &finalSynthesisParameters, &loop); if (pSignalUnitRack == NULL) { // stop the rendering if volume EG is finished @@ -530,25 +607,36 @@ }*/ // TODO: ^^^ - pSignalUnitRack->Increment(); + if (!delay) pSignalUnitRack->Increment(); } Pos = newPos; i = iSubFragmentEnd; } + + if (delay) return; if (bVoiceRequiresDedicatedRouting) { + if (bEq) { + pEq->RenderAudio(Samples); + pEq->GetOutChannelLeft()->CopyTo(GetEngine()->pDedicatedVoiceChannelLeft, Samples); + pEq->GetOutChannelRight()->CopyTo(GetEngine()->pDedicatedVoiceChannelRight, Samples); + } optional effectSendLevels[2] = { pMidiKeyInfo->ReverbSend, pMidiKeyInfo->ChorusSend }; GetEngine()->RouteDedicatedVoiceChannels(pEngineChannel, effectSendLevels, Samples); + } else if (bEq) { + pEq->RenderAudio(Samples); + pEq->GetOutChannelLeft()->MixTo(pChannel->pChannelLeft, Samples); + pEq->GetOutChannelRight()->MixTo(pChannel->pChannelRight, Samples); } } /** - * 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 @@ -576,10 +664,14 @@ 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_channel_pressure) { + ProcessChannelPressureEvent(itEvent); + } else if (itEvent->Type == Event::type_note_pressure) { + ProcessPolyphonicKeyPressureEvent(itEvent); } ProcessCCEvent(itEvent); @@ -604,8 +696,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 @@ -614,9 +706,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); @@ -625,6 +717,35 @@ } } } + // 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; + } + } } } @@ -651,7 +772,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: } @@ -678,12 +799,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; @@ -691,6 +812,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 @@ -703,7 +837,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); }