Files
UnrealEngine/Engine/Source/Runtime/SignalProcessing/Private/WaveTableOsc.cpp
2025-05-18 13:04:45 +08:00

284 lines
7.0 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "DSP/WaveTableOsc.h"
namespace Audio
{
FWaveTableOsc::FWaveTableOsc()
: FrequencyHz(440.0f)
, SampleRate(44100)
, NormalPhaseReadIndex(0.0f)
, QuadPhaseReadIndex(0.0f)
, PhaseIncrement(0.0f)
, OutputScale(1.0f)
, OutputAdd(0.0f)
, WaveTableType(EWaveTable::None)
{
SetFrequencyHz(FrequencyHz);
}
FWaveTableOsc::~FWaveTableOsc()
{
}
void FWaveTableOsc::Init(const float InSampleRate, const float InFrequencyHz)
{
SampleRate = InSampleRate;
FrequencyHz = InFrequencyHz;
Reset();
UpdateFrequency();
}
void FWaveTableOsc::SetSampleRate(const float InSampleRate)
{
SampleRate = InSampleRate;
UpdateFrequency();
}
void FWaveTableOsc::SetScaleAdd(const float InScale, const float InAdd)
{
OutputScale = InScale;
OutputAdd = InAdd;
}
void FWaveTableOsc::Reset()
{
// Normal phase starts at front of table
NormalPhaseReadIndex = 0.0f;
// Quad phase starts at 25% of the wave table buffer size
QuadPhaseReadIndex = 0.25f * WaveTableBuffer.Num();
}
// Sets the frequency of the wave table oscillator.
void FWaveTableOsc::SetFrequencyHz(const float InFrequencyHz)
{
FrequencyHz = InFrequencyHz;
UpdateFrequency();
}
void FWaveTableOsc::UpdateFrequency()
{
PhaseIncrement = (float)WaveTableBuffer.Num() * FrequencyHz / (float)SampleRate;
}
TArray<float>& FWaveTableOsc::GetTable()
{
return WaveTableBuffer;
}
const TArray<float>& FWaveTableOsc::GetTable() const
{
return WaveTableBuffer;
}
void FWaveTableOsc::Generate(float* OutputNormalPhase, float* OutputQuadPhase)
{
const int32 NormPhaseReadIndexPrev = (int32)NormalPhaseReadIndex;
const float Alpha = NormalPhaseReadIndex - NormPhaseReadIndexPrev;
if (OutputNormalPhase)
{
const int32 NormPhaseReadIndexNext = (NormPhaseReadIndexPrev + 1) % WaveTableBuffer.Num();
const float NormalPhaseOutput = FMath::Lerp(WaveTableBuffer[NormPhaseReadIndexPrev], WaveTableBuffer[NormPhaseReadIndexNext], Alpha);
*OutputNormalPhase = NormalPhaseOutput * OutputScale + OutputAdd;
}
if (OutputQuadPhase)
{
const int32 QuadPhaseReadIndexPrev = (int32)QuadPhaseReadIndex;
const int32 QuadPhaseReadIndexNext = (QuadPhaseReadIndexPrev + 1) % WaveTableBuffer.Num();
float QuadPhaseOutput = FMath::Lerp(WaveTableBuffer[QuadPhaseReadIndexPrev], WaveTableBuffer[QuadPhaseReadIndexNext], Alpha);
*OutputQuadPhase = QuadPhaseOutput * OutputScale + OutputAdd;
}
NormalPhaseReadIndex += PhaseIncrement;
QuadPhaseReadIndex += PhaseIncrement;
while (NormalPhaseReadIndex >= WaveTableBuffer.Num())
{
NormalPhaseReadIndex -= WaveTableBuffer.Num();
}
while (QuadPhaseReadIndex >= WaveTableBuffer.Num())
{
QuadPhaseReadIndex -= WaveTableBuffer.Num();
}
}
TSharedPtr<FWaveTableOsc> FWaveTableOsc::CreateWaveTable(const EWaveTable::Type WaveTableType, const int32 WaveTableSize)
{
if (WaveTableType == EWaveTable::None || WaveTableSize <= 0)
{
return nullptr;
}
// Make a new wave table oscillator
TSharedPtr<FWaveTableOsc> NewWaveTableOsc = TSharedPtr<FWaveTableOsc>(new FWaveTableOsc());
NewWaveTableOsc->WaveTableBuffer.AddDefaulted(WaveTableSize);
NewWaveTableOsc->WaveTableType = WaveTableType;
NewWaveTableOsc->Reset();
switch (WaveTableType)
{
case EWaveTable::SineWaveTable:
{
for (int32 i = 0; i < WaveTableSize; ++i)
{
NewWaveTableOsc->WaveTableBuffer[i] = FMath::Sin(2.0f * PI * (float)i / WaveTableSize);
}
}
break;
case EWaveTable::SawWaveTable:
{
const int32 HalfTableSize = WaveTableSize / 2;
const float Slope = 1.0f / HalfTableSize;
// Rise to half-point, the offset by 1.0, continue to rise
for (int32 i = 0; i < WaveTableSize; ++i)
{
if (i < HalfTableSize)
{
NewWaveTableOsc->WaveTableBuffer[i] = Slope * i;
}
else
{
NewWaveTableOsc->WaveTableBuffer[i] = Slope * (i - HalfTableSize - 1) - 1.0f;
}
}
}
break;
case EWaveTable::TriangleWaveTable:
{
const int32 QuarterTableSize = WaveTableSize / 4;
const int32 HalfTableSize = WaveTableSize / 2;
const int32 ThreeQuarterTableSize = 3 * WaveTableSize / 4;
const float SlopeUp = 1.0f / QuarterTableSize;
const float SlopeDown = -2.0f / HalfTableSize;
for (int32 i = 0; i < WaveTableSize; ++i)
{
if (i < QuarterTableSize)
{
NewWaveTableOsc->WaveTableBuffer[i] = SlopeUp * i - 1.0f;
}
else if (i < ThreeQuarterTableSize)
{
NewWaveTableOsc->WaveTableBuffer[i] = SlopeDown * (i - QuarterTableSize) + 1.0f;
}
else
{
NewWaveTableOsc->WaveTableBuffer[i] = SlopeUp * (i - ThreeQuarterTableSize) - 1.0f;
}
}
}
break;
case EWaveTable::SquareWaveTable:
{
const int32 HalfTableSize = WaveTableSize / 2;
for (int32 i = 0; i < WaveTableSize; ++i)
{
if (i < HalfTableSize)
{
NewWaveTableOsc->WaveTableBuffer[i] = 1.0f;
}
else
{
NewWaveTableOsc->WaveTableBuffer[i] = -1.0f;
}
}
}
break;
case EWaveTable::BandLimitedSawWaveTable:
{
float MaxSample = 0.0f;
for (int32 i = 0; i < WaveTableSize; ++i)
{
NewWaveTableOsc->WaveTableBuffer[i] = 0.0f;
for (int32 g = 1; g <= 6; ++g)
{
NewWaveTableOsc->WaveTableBuffer[i] += FMath::Pow(-1.0f, (float)(g + 1)) * (1.0f / g) * FMath::Sin(2.0f * PI * i * (float)g / WaveTableSize);
}
if (NewWaveTableOsc->WaveTableBuffer[i] > MaxSample)
{
MaxSample = NewWaveTableOsc->WaveTableBuffer[i];
}
}
for (int32 i = 0; i < WaveTableSize; ++i)
{
NewWaveTableOsc->WaveTableBuffer[i] /= MaxSample;
}
}
break;
case EWaveTable::BandLimitedTriangleWaveTable:
{
float MaxSample = 0.0f;
for (int32 i = 0; i < WaveTableSize; ++i)
{
NewWaveTableOsc->WaveTableBuffer[i] = 0.0f;
for (int g = 1; g <= 6; ++g)
{
NewWaveTableOsc->WaveTableBuffer[i] += FMath::Pow(-1.0f, (float)g) * (1.0f / FMath::Pow(2.0f * (float)g + 1.0f, 2.0f)) * FMath::Sin(2.0f * PI * (2.0f * (float)g + 1.0f) * i / WaveTableSize);
}
if (NewWaveTableOsc->WaveTableBuffer[i] > MaxSample)
{
MaxSample = NewWaveTableOsc->WaveTableBuffer[i];
}
}
for (int32 i = 0; i < WaveTableSize; ++i)
{
NewWaveTableOsc->WaveTableBuffer[i] /= MaxSample;
}
}
break;
case EWaveTable::BandLimitedSquareWaveTable:
{
float MaxSample = 0.0f;
for (int32 i = 0; i < WaveTableSize; ++i)
{
NewWaveTableOsc->WaveTableBuffer[i] = 0.0f;
for (int g = 1; g <= 6; ++g)
{
NewWaveTableOsc->WaveTableBuffer[i] += (1.0f / g) * (float)FMath::Sin(2.0f * PI * i * (float)g / WaveTableSize);
}
if (NewWaveTableOsc->WaveTableBuffer[i] > MaxSample)
{
MaxSample = NewWaveTableOsc->WaveTableBuffer[i];
}
}
for (int32 i = 0; i < WaveTableSize; ++i)
{
NewWaveTableOsc->WaveTableBuffer[i] /= MaxSample;
}
}
break;
case EWaveTable::Custom:
{
// don't do anything, the caller will fill in the table themselves
}
break;
}
return NewWaveTableOsc;
}
}