Files
UnrealEngine/Engine/Source/Runtime/SignalProcessing/Public/DSP/Granulator.h
2025-05-18 13:04:45 +08:00

354 lines
10 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "DSP/Dsp.h"
#include "DSP/Osc.h"
#include "DSP/SampleBufferReader.h"
#include "DSP/Envelope.h"
#include "DSP/Amp.h"
#include "DSP/DynamicsProcessor.h"
#include "SampleBuffer.h"
namespace Audio
{
enum class EGranularSynthMode : uint8
{
Synthesis,
Granulation,
Count,
};
enum class EGrainEnvelopeType
{
Rectangular,
Triangle,
DownwardTriangle,
UpwardTriangle,
ExponentialDecay,
ExponentialIncrease,
Gaussian,
Hanning,
Lanczos,
Cosine,
CosineSquared,
Welch,
Blackman,
BlackmanHarris,
Count
};
// Simple class that generates an envelope and lets you retrieve interpolated values at any given fraction
class FGrainEnvelope
{
public:
SIGNALPROCESSING_API FGrainEnvelope();
SIGNALPROCESSING_API ~FGrainEnvelope();
SIGNALPROCESSING_API void GenerateEnvelope(const EGrainEnvelopeType EnvelopeType, const int32 NumFrames);
SIGNALPROCESSING_API float GetValue(const float Fraction) const;
private:
EGrainEnvelopeType CurrentType;
TArray<float> GrainEnvelope;
};
struct FGrainData
{
// What oscillator type to use
EOsc::Type OscType;
// Where in the buffer to seek the grain playback to
float BufferSeekTime;
// Duration of the grain
float DurationSeconds;
// Grain pitch scale (if in buffer reading mode)
float PitchScale;
// The grain frequency (if in synthesis mode)
float Frequency;
// Grain volume
float Volume;
// Grain pan
float Pan;
};
class FGranularSynth;
// Class representing a grain of audio
class FGrain
{
public:
SIGNALPROCESSING_API FGrain(const int32 InGrainId, FGranularSynth* InParent);
SIGNALPROCESSING_API ~FGrain();
// Plays the grain with the supplied grain data
SIGNALPROCESSING_API void Play(const FGrainData& InGrainData);
// Changes the oscillator type for the grain (if the grain is in synth mode)
SIGNALPROCESSING_API void SetOscType(const EOsc::Type InType);
// Sets the oscillator frequency
SIGNALPROCESSING_API void SetOscFrequency(const float InFrequency);
// Sets the oscillator frequency modulation
SIGNALPROCESSING_API void SetOscFrequencyModuation(const float InFrequencyModulation);
// Sets the grain pitch modulation
SIGNALPROCESSING_API void SetPitchModulation(const float InPitchModulation);
// Sets the grain modulation
SIGNALPROCESSING_API void SetVolumeModulation(const float InVolumeModulation);
// Sets the pan modulation angle
SIGNALPROCESSING_API void SetPanModulation(const float InPanModulation);
// Changes how quickly the grain reads through envelope
SIGNALPROCESSING_API void SetDurationScale(const float InDurationScale);
// Queries if this grain is finished playing and needs to be reaped
SIGNALPROCESSING_API bool IsDone() const;
// Generates the next frame from the grain
SIGNALPROCESSING_API bool GenerateFrame(float* OutStereoFrame);
protected:
SIGNALPROCESSING_API float GetEnvelopeValue();
// Grain id
int32 GrainId;
// Parent synth
FGranularSynth* Parent;
// Grain data struct
FGrainData GrainData;
// The sample buffer reader to use for the grain of the mode is granulation
FSampleBufferReader SampleBufferReader;
// Oscillator to use for synthesis mode
FOsc Osc;
// The current pitch
float CurrentPitch;
// Current frequency
float CurrentFrequency;
// The current volume scale
float CurrentVolumeScale;
// The current pan
float CurrentPan;
// How quickly we read through the envelope
float DurationScale;
// The current frame count the grain is on
float CurrentFrameCount;
// The end frame count the grain will finish on
float EndFrameCount;
// Speaker map based on the current grain azimuth
TArray<float> SpeakerMap;
// Scratch buffer for sample reading
TArray<float> FrameScratch;
};
// A stereo granulator
class FGranularSynth
{
public:
SIGNALPROCESSING_API FGranularSynth();
SIGNALPROCESSING_API ~FGranularSynth();
SIGNALPROCESSING_API void Init(const int32 InSampleRate, const int32 InNumInitialGrains);
// Loads a sound wave to use for granular synth mode
SIGNALPROCESSING_API void LoadSampleBuffer(const TSampleBuffer<int16>& InSampleBuffer);
// Plays a granular synthesis "Note"
SIGNALPROCESSING_API void NoteOn(const uint32 InMidiNote, const float InVelocity, const float InDurationSec = INDEX_NONE);
// Note off, triggers release envelope
SIGNALPROCESSING_API void NoteOff(const uint32 InMidiNote, const bool bKill);
// Sets the granular synth attack time
SIGNALPROCESSING_API void SetAttackTime(const float InAttackTimeMSec);
// Sets the granular synth decay time
SIGNALPROCESSING_API void SetDecayTime(const float InDecayTimeMSec);
// Sets the granular synth sustain gain
SIGNALPROCESSING_API void SetSustainGain(const float InSustainGain);
// Sets the granular synth releas etime
SIGNALPROCESSING_API void SetReleaseTime(const float InReleaseTimeMSec);
// Seeks the loaded buffer used for granulation. Grains will spawn from this location
SIGNALPROCESSING_API void SeekTime(const float InTimeSec, const float LerpTimeSec = 0.0f, const ESeekType::Type InSeekType = ESeekType::FromBeginning);
// Sets whether or not the buffer playback advances on its own or if it just sits in one place
SIGNALPROCESSING_API void SetScrubMode(const bool bIsScrubMode);
// Sets how fast the granular play head for granulation is is played (and in what direction)
SIGNALPROCESSING_API void SetPlaybackSpeed(const float InPlaybackSpeed);
// The rate at which new grains are attempted to be spawned
SIGNALPROCESSING_API void SetGrainsPerSecond(const float InNumberOfGrainsPerSecond);
// The probability at which a grain will occur when a grain tries to spawn. Allows for sporatic grain generation.
SIGNALPROCESSING_API void SetGrainProbability(const float InGrainProbability);
// Sets the envelope type to use for new grains. Will instantly switch all grains to this envelope type so may cause discontinuities if switched while playing.
SIGNALPROCESSING_API void SetGrainEnvelopeType(const EGrainEnvelopeType InGrainEnvelopeType);
// Sets the grain oscillator type (for use with granular synthesis mode)
SIGNALPROCESSING_API void SetGrainOscType(const EOsc::Type InGrainOscType);
// Sets the base grain volume and randomization range
SIGNALPROCESSING_API void SetGrainVolume(const float InBaseVolume, const FVector2D InVolumeRange = FVector2D::ZeroVector);
// Sets the grain modulation -- allows modulating actively playing grain volumes
SIGNALPROCESSING_API void SetGrainVolumeModulation(const float InVolumeModulation);
// Sets the base grain pitch and randomization range
SIGNALPROCESSING_API void SetGrainPitch(const float InBasePitch, const FVector2D InPitchRange = FVector2D::ZeroVector);
// Sets the grain frequency
SIGNALPROCESSING_API void SetGrainFrequency(const float InFrequency, const FVector2D InPitchRange = FVector2D::ZeroVector);
// Sets the grain frequency modulation
SIGNALPROCESSING_API void SetGrainFrequencyModulation(const float InFrequencyModulation);
// Sets the grain pitch modulation -- allows modulating actively playing grain pitches
SIGNALPROCESSING_API void SetGrainPitchModulation(const float InPitchModulation);
// Sets the grain azimuth (pan) and randomization range
SIGNALPROCESSING_API void SetGrainPan(const float InBasePan, const FVector2D InPanRange = FVector2D::ZeroVector);
// Sets the grain azimuth modulation - allows modulating actively playing grain azimuths
SIGNALPROCESSING_API void SetGrainPanModulation(const float InPanModulation);
// Sets the grain duration.
SIGNALPROCESSING_API void SetGrainDuration(const float InBaseDuration, const FVector2D InDurationRange = FVector2D::ZeroVector);
// Sets the grain duration modulation.
SIGNALPROCESSING_API void SetGrainDurationScale(const float InDurationScale);
// Return the number of currently active grains
SIGNALPROCESSING_API int32 GetNumActiveGrains() const;
// Get current playback time (in granular mode)
SIGNALPROCESSING_API float GetCurrentPlayheadTime() const;
// Returns the duration of the internal loaded sample buffer
SIGNALPROCESSING_API float GetSampleDuration() const;
// Generate the next audio buffer
SIGNALPROCESSING_API void Generate(float* OutAudiobuffer, const int32 NumFrames);
protected:
// Spawns grains
SIGNALPROCESSING_API void SpawnGrain();
// Return wrapped playhead position
SIGNALPROCESSING_API float GetWrappedPlayheadPosition(float PlayheadFrame);
// Current grain azimuth modulation
struct FGrainParam
{
float Modulation;
float Base;
FVector2D Range;
float GetValue() const
{
return Base + FMath::FRandRange(Range.X, Range.Y);
}
float GetModulation() const
{
return Modulation;
}
FGrainParam()
: Modulation(0.0f)
, Base(0.0f)
{}
};
int32 SampleRate;
int32 NumChannels;
// The single envelope function used by all grains
FGrainEnvelope GrainEnvelope;
// What mode the granular synthesizer is in
EGranularSynthMode Mode;
// The oscillator type to use if in synthesis mode
EOsc::Type GrainOscType;
// Current grain envelope type
EGrainEnvelopeType GrainEnvelopeType;
// A pool of free grains. Will dynamically grow to needed grains based on grain density.
TArray<FGrain> GrainPool;
TArray<int32> FreeGrains;
TArray<int32> ActiveGrains;
TArray<int32> DeadGrains;
// The rate at which grains are spawned
float GrainsPerSecond;
// The probability of a grain occurring when it tries to spawn (based off the GrainsPerSecond)
float GrainProbability;
// The current number of frames since last attempt to spawn
int32 CurrentSpawnFrameCount;
// The next frame when a grain needs to spawn
int32 NextSpawnFrame;
// Counts for overall note duration of the granulator
int32 NoteDurationFrameCount;
int32 NoteDurationFrameEnd;
FGrainParam Pan;
FGrainParam Volume;
FGrainParam Pitch;
FGrainParam Frequency;
FGrainParam Duration;
// Overall envelope of the granulator
FEnvelope GainEnv;
FAmp Amp;
FDynamicsProcessor DynamicsProcessor;
// The buffer which holds the sample to be granulated
TSampleBuffer<int16> SampleBuffer;
// The current playhead frame
float CurrentPlayHeadFrame;
float PlaybackSpeed;
int32 NumActiveGrains;
bool bScrubMode;
FLinearEase SeekingPlayheadTimeFrame;
friend class FGrain;
};
}