--- linuxsampler/trunk/src/engines/common/AbstractVoice.cpp 2011/07/25 17:21:16 2216 +++ linuxsampler/trunk/src/engines/common/AbstractVoice.cpp 2014/05/18 17:38:25 2559 @@ -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-2012 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 * @@ -26,7 +26,7 @@ namespace LinuxSampler { - AbstractVoice::AbstractVoice() { + AbstractVoice::AbstractVoice(SignalUnitRack* pRack): pSignalUnitRack(pRack) { pEngineChannel = NULL; pLFO1 = new LFOUnsigned(1.0f); // amplitude LFO (0..1 range) pLFO2 = new LFOUnsigned(1.0f); // filter LFO (0..1 range) @@ -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); } /** @@ -99,6 +111,7 @@ Type = VoiceType; MIDIKey = itNoteOnEvent->Param.Note.Key; + MIDIVelocity = itNoteOnEvent->Param.Note.Velocity; PlaybackState = playback_state_init; // mark voice as triggered, but no audio rendered yet Delay = itNoteOnEvent->FragmentPos(); itTriggerEvent = itNoteOnEvent; @@ -111,6 +124,8 @@ RgnInfo = GetRegionInfo(); InstrInfo = GetInstrumentInfo(); + MIDIPan = CalculatePan(pEngineChannel->iLastPanRequest); + AboutToTrigger(); // calculate volume @@ -126,14 +141,12 @@ // 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; CrossfadeSmoother.trigger(crossfadeVolume, subfragmentRate); VolumeSmoother.trigger(pEngineChannel->MidiVolume, subfragmentRate); - PanLeftSmoother.trigger(pEngineChannel->GlobalPanLeft, subfragmentRate); - PanRightSmoother.trigger(pEngineChannel->GlobalPanRight, subfragmentRate); // Check if the sample needs disk streaming or is too short for that long cachedsamples = GetSampleCacheSize() / SmplInfo.FrameSize; @@ -177,7 +190,7 @@ // the length of the decay and release curves are dependent on the velocity const double velrelease = 1 / GetVelocityRelease(itNoteOnEvent->Param.Note.Velocity); - if (GetSignalUnitRack() == NULL) { // setup EG 1 (VCA EG) + if (pSignalUnitRack == NULL) { // setup EG 1 (VCA EG) // get current value of EG1 controller double eg1controllervalue = GetEG1ControllerValue(itNoteOnEvent->Param.Note.Velocity); @@ -186,9 +199,14 @@ TriggerEG1(egInfo, velrelease, velocityAttenuation, GetEngine()->SampleRate, itNoteOnEvent->Param.Note.Velocity); } else { - GetSignalUnitRack()->Trigger(); + pSignalUnitRack->Trigger(); } + uint8_t pan = MIDIPan; + if (pSignalUnitRack) pan = pSignalUnitRack->GetEndpointUnit()->CalculatePan(MIDIPan); + PanLeftSmoother.trigger(AbstractEngine::PanCurve[128 - pan], subfragmentRate); + PanRightSmoother.trigger(AbstractEngine::PanCurve[pan], subfragmentRate); + #ifdef CONFIG_INTERPOLATE_VOLUME // setup initial volume in synthesis parameters #ifdef CONFIG_PROCESS_MUTED_CHANNELS @@ -200,19 +218,19 @@ #else { float finalVolume; - if (GetSignalUnitRack() == NULL) { + if (pSignalUnitRack == NULL) { finalVolume = pEngineChannel->MidiVolume * crossfadeVolume * pEG1->getLevel(); } else { - finalVolume = pEngineChannel->MidiVolume * crossfadeVolume * GetSignalUnitRack()->GetEndpointUnit()->GetVolume(); + 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 - if (GetSignalUnitRack() == NULL) { + if (pSignalUnitRack == NULL) { // setup EG 2 (VCF Cutoff EG) { // get current value of EG2 controller @@ -291,6 +309,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 } @@ -309,14 +336,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); 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,7 +382,7 @@ GetFirstEventOnKey(MIDIKey, 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; @@ -356,10 +408,10 @@ // drivers that use Samples < MaxSamplesPerCycle). // End the EG1 here, at pos 0, with a shorter max fade // out time. - if (GetSignalUnitRack() == NULL) { + if (pSignalUnitRack == NULL) { pEG1->enterFadeOutStage(Samples / CONFIG_DEFAULT_SUBFRAGMENT_SIZE); } else { - // TODO: + pSignalUnitRack->EnterFadeOutStage(Samples / CONFIG_DEFAULT_SUBFRAGMENT_SIZE); } itKillEvent = Pool::Iterator(); } else { @@ -375,8 +427,13 @@ 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()->CalculatePan(MIDIPan); + + PanLeftSmoother.update(AbstractEngine::PanCurve[128 - pan]); + PanRightSmoother.update(AbstractEngine::PanCurve[pan]); finalSynthesisParameters.fFinalPitch = Pitch.PitchBase * Pitch.PitchBend; float fFinalVolume = VolumeSmoother.render() * CrossfadeSmoother.render(); @@ -387,8 +444,8 @@ // process transition events (note on, note off & sustain pedal) processTransitionEvents(itNoteEvent, iSubFragmentEnd); processGroupEvents(itGroupEvent, iSubFragmentEnd); - - if (GetSignalUnitRack() == NULL) { + + if (pSignalUnitRack == NULL) { // 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) || @@ -434,22 +491,26 @@ if (bLFO2Enabled) fFinalCutoff *= 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(); }*/ // TODO: ^^^ - fFinalVolume *= GetSignalUnitRack()->GetEndpointUnit()->GetVolume(); - fFinalCutoff = GetSignalUnitRack()->GetEndpointUnit()->CalculateFilterCutoff(fFinalCutoff); - fFinalResonance = GetSignalUnitRack()->GetEndpointUnit()->CalculateResonance(fFinalResonance); + fFinalVolume *= pSignalUnitRack->GetEndpointUnit()->GetVolume(); + fFinalCutoff = pSignalUnitRack->GetEndpointUnit()->CalculateFilterCutoff(fFinalCutoff); + fFinalResonance = pSignalUnitRack->GetEndpointUnit()->CalculateResonance(fFinalResonance); finalSynthesisParameters.fFinalPitch = - GetSignalUnitRack()->GetEndpointUnit()->CalculatePitch(finalSynthesisParameters.fFinalPitch); + pSignalUnitRack->GetEndpointUnit()->CalculatePitch(finalSynthesisParameters.fFinalPitch); } @@ -485,19 +546,19 @@ fFinalVolume * VolumeRight * PanRightSmoother.render(); #endif // render audio for one subfragment - RunSynthesisFunction(SynthesisMode, &finalSynthesisParameters, &loop); + if (!delay) RunSynthesisFunction(SynthesisMode, &finalSynthesisParameters, &loop); - if (GetSignalUnitRack() == NULL) { + if (pSignalUnitRack == NULL) { // stop the rendering if volume EG is finished if (pEG1->getSegmentType() == EG::segment_end) break; } else { // stop the rendering if the endpoint unit is not active - if (!GetSignalUnitRack()->GetEndpointUnit()->Active()) break; + if (!pSignalUnitRack->GetEndpointUnit()->Active()) break; } const double newPos = Pos + (iSubFragmentEnd - i) * finalSynthesisParameters.fFinalPitch; - if (GetSignalUnitRack() == NULL) { + if (pSignalUnitRack == NULL) { // increment envelopes' positions if (pEG1->active()) { @@ -522,25 +583,36 @@ }*/ // TODO: ^^^ - GetSignalUnitRack()->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 @@ -554,7 +626,7 @@ if (itEvent->Param.CC.Controller == VCFResonanceCtrl.controller) { processResonanceEvent(itEvent); } - if (GetSignalUnitRack() == NULL) { + if (pSignalUnitRack == NULL) { if (itEvent->Param.CC.Controller == pLFO1->ExtController) { pLFO1->update(itEvent->Param.CC.Value); } @@ -568,16 +640,19 @@ if (itEvent->Param.CC.Controller == 7) { // volume VolumeSmoother.update(AbstractEngine::VolumeCurve[itEvent->Param.CC.Value]); } else if (itEvent->Param.CC.Controller == 10) { // panpot - PanLeftSmoother.update(AbstractEngine::PanCurve[128 - itEvent->Param.CC.Value]); - PanRightSmoother.update(AbstractEngine::PanCurve[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); - if (GetSignalUnitRack() != NULL) { - GetSignalUnitRack()->ProcessCCEvent(itEvent); + if (pSignalUnitRack != NULL) { + pSignalUnitRack->ProcessCCEvent(itEvent); } } } @@ -610,11 +685,11 @@ if (itEvent->Type == Event::type_release) { EnterReleaseStage(); } else if (itEvent->Type == Event::type_cancel_release) { - if (GetSignalUnitRack() == NULL) { + 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); } else { - GetSignalUnitRack()->CancelRelease(); + pSignalUnitRack->CancelRelease(); } } } @@ -642,7 +717,7 @@ * @param itNoteOffEvent - event which causes this voice to die soon */ void AbstractVoice::UpdatePortamentoPos(Pool::Iterator& itNoteOffEvent) { - if (GetSignalUnitRack() == NULL) { + if (pSignalUnitRack == NULL) { const float fFinalEG3Level = EG3.level(itNoteOffEvent->FragmentPos()); pEngineChannel->PortamentoPos = (float) MIDIKey + RTMath::FreqRatioToCents(fFinalEG3Level) * 0.01f; } else { @@ -684,6 +759,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 @@ -709,19 +797,19 @@ } void AbstractVoice::EnterReleaseStage() { - if (GetSignalUnitRack() == NULL) { + if (pSignalUnitRack == NULL) { pEG1->update(EG::event_release, GetEngine()->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE); pEG2->update(EG::event_release, GetEngine()->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE); } else { - GetSignalUnitRack()->EnterReleaseStage(); + pSignalUnitRack->EnterReleaseStage(); } } bool AbstractVoice::EG1Finished() { - if (GetSignalUnitRack() == NULL) { + if (pSignalUnitRack == NULL) { return pEG1->getSegmentType() == EG::segment_end; } else { - return !GetSignalUnitRack()->GetEndpointUnit()->Active(); + return !pSignalUnitRack->GetEndpointUnit()->Active(); } }