You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#rnx #rb none #ROBOMERGE-OWNER: ryan.durand #ROBOMERGE-AUTHOR: ryan.durand #ROBOMERGE-SOURCE: CL 10869210 via CL 10869511 via CL 10869900 #ROBOMERGE-BOT: (v613-10869866) [CL 10870549 by ryan durand in Main branch]
216 lines
4.2 KiB
C++
216 lines
4.2 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "DSP/LFO.h"
|
|
#include "DSP/Dsp.h"
|
|
|
|
namespace Audio
|
|
{
|
|
FLFO::FLFO()
|
|
: LFOType(ELFO::Sine)
|
|
, LFOMode(ELFOMode::Sync)
|
|
, ExponentialFactor(3.5f)
|
|
, RSHCounter(INDEX_NONE)
|
|
, RSHValue(0.0f)
|
|
, ModScale(1.0f)
|
|
, ModAdd(0.0f)
|
|
, LastOutput(0.0f)
|
|
, QuadLastOutput(0.0f)
|
|
{
|
|
}
|
|
|
|
FLFO::~FLFO()
|
|
{
|
|
}
|
|
|
|
void FLFO::Init(const float InSampleRate, const int32 InVoiceId, FModulationMatrix* InMatrix, const int32 ModMatrixStage)
|
|
{
|
|
IOscBase::Init(InSampleRate, InVoiceId, InMatrix, ModMatrixStage);
|
|
|
|
if (ModMatrix)
|
|
{
|
|
ModNormalPhase = ModMatrix->CreatePatchSource(InVoiceId);
|
|
ModQuadPhase = ModMatrix->CreatePatchSource(InVoiceId);
|
|
|
|
#if MOD_MATRIX_DEBUG_NAMES
|
|
ModNormalPhase.Name = TEXT("ModNormalPhase");
|
|
ModQuadPhase.Name = TEXT("ModQuadPhase");
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void FLFO::Start()
|
|
{
|
|
if (LFOMode == ELFOMode::Sync || LFOMode == ELFOMode::OneShot)
|
|
{
|
|
Reset();
|
|
}
|
|
|
|
bIsPlaying = true;
|
|
}
|
|
|
|
void FLFO::Stop()
|
|
{
|
|
bIsPlaying = false;
|
|
}
|
|
|
|
void FLFO::Reset()
|
|
{
|
|
// reset base class first
|
|
IOscBase::Reset();
|
|
|
|
RSHValue = 0.0f;
|
|
RSHCounter = INDEX_NONE;
|
|
}
|
|
|
|
float FLFO::Generate(float* QuadPhaseOutput)
|
|
{
|
|
// If the LFO isn't playing, return 0.0 for quad phase and for normal output
|
|
if (!bIsPlaying)
|
|
{
|
|
if (QuadPhaseOutput)
|
|
{
|
|
*QuadPhaseOutput = QuadLastOutput;
|
|
}
|
|
|
|
return LastOutput;
|
|
}
|
|
|
|
const bool bWrapped = WrapPhase();
|
|
|
|
// If we're in the oneshot mode, check if we've wrapped
|
|
// and if so, turn the LFO off and return 0.0s
|
|
if (LFOMode == ELFOMode::OneShot && bWrapped)
|
|
{
|
|
bIsPlaying = false;
|
|
|
|
if (QuadPhaseOutput)
|
|
{
|
|
*QuadPhaseOutput = QuadLastOutput;
|
|
}
|
|
|
|
return LastOutput;
|
|
}
|
|
|
|
LastOutput = ComputeLFO(GetPhase(), QuadPhaseOutput);
|
|
|
|
// Update the LFO phase after computing LFO values
|
|
UpdatePhase();
|
|
|
|
// Return the output
|
|
return LastOutput;
|
|
}
|
|
|
|
float FLFO::ComputeLFO(const float InPhase, float* OutQuad)
|
|
{
|
|
float Output = 0.0f;
|
|
float QuadOutput = 0.0f;
|
|
|
|
float QuadPhase = InPhase + 0.25f;
|
|
if (QuadPhase >= 1.0f)
|
|
{
|
|
QuadPhase -= 1.0f;
|
|
}
|
|
|
|
switch (LFOType)
|
|
{
|
|
case ELFO::Sine:
|
|
{
|
|
float Angle = 2.0f * InPhase * PI - PI;
|
|
Output = Audio::FastSin(Angle);
|
|
|
|
Angle = 2.0f * QuadPhase * PI - PI;
|
|
QuadOutput = Audio::FastSin(Angle);
|
|
}
|
|
break;
|
|
|
|
case ELFO::UpSaw:
|
|
{
|
|
Output = GetBipolar(InPhase);
|
|
QuadOutput = GetBipolar(QuadPhase);
|
|
}
|
|
break;
|
|
|
|
case ELFO::DownSaw:
|
|
{
|
|
Output = -1.0f * GetBipolar(InPhase);
|
|
QuadOutput = -1.0f * GetBipolar(QuadPhase);
|
|
}
|
|
break;
|
|
|
|
case ELFO::Square:
|
|
{
|
|
Output = InPhase > PulseWidth ? -1.0f : 1.0f;
|
|
QuadOutput = QuadPhase > PulseWidth ? -1.0f : 1.0f;
|
|
}
|
|
break;
|
|
|
|
case ELFO::Triangle:
|
|
{
|
|
Output = FMath::Abs(GetBipolar(InPhase));
|
|
QuadOutput = FMath::Abs(GetBipolar(QuadPhase));;
|
|
|
|
// If not one-shot, we need to convert to bipolar
|
|
if (LFOMode != ELFOMode::OneShot)
|
|
{
|
|
Output = GetBipolar(Output);
|
|
QuadOutput = GetBipolar(QuadOutput);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ELFO::Exponential:
|
|
{
|
|
Output = FMath::Pow(InPhase, ExponentialFactor);
|
|
QuadOutput = FMath::Pow(QuadPhase, ExponentialFactor);
|
|
}
|
|
break;
|
|
|
|
case ELFO::RandomSampleHold:
|
|
{
|
|
const float FrequencyThreshold = SampleRate / Freq;
|
|
if (RSHCounter > (uint32)FrequencyThreshold)
|
|
{
|
|
RSHCounter = 0;
|
|
RSHValue = FMath::FRandRange(-1.0f, 1.0f);
|
|
}
|
|
else
|
|
{
|
|
++RSHCounter;
|
|
}
|
|
|
|
Output = RSHValue;
|
|
QuadOutput = RSHValue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
const float MaxGain = Gain * ExternalGainMod;
|
|
Output = Output * MaxGain;
|
|
QuadOutput = QuadOutput * MaxGain;
|
|
|
|
// If we have a mod matrix, then mix in the destination data
|
|
// This allows LFO's (or envelopes, etc) to modulation this LFO
|
|
if (ModMatrix)
|
|
{
|
|
ModMatrix->GetDestinationValue(VoiceId, ModScaleDest, ModAdd);
|
|
ModMatrix->GetDestinationValue(VoiceId, ModAddDest, ModScale);
|
|
|
|
Output = Output * ModScale + ModAdd;
|
|
QuadOutput = QuadOutput * ModScale + ModAdd;
|
|
|
|
// Write out the modulations
|
|
ModMatrix->SetSourceValue(VoiceId, ModNormalPhase, Output);
|
|
ModMatrix->SetSourceValue(VoiceId, ModQuadPhase, QuadOutput);
|
|
}
|
|
|
|
QuadLastOutput = QuadOutput;
|
|
|
|
if (OutQuad)
|
|
{
|
|
*OutQuad = QuadOutput;
|
|
}
|
|
|
|
return Output;
|
|
}
|
|
}
|