Here is the source code for the 2 classes. If you add these to a project you should be able to then create the SoundNodeProceduralTest inside a Sound Cue and connect it’s output wire to the speaker, hit play cue, and here the tone that is created (plus the “click” that I’m trying to debug).
// File: SoundNodeProceduralTest.h
#pragma once
#include "Sound/SoundNode.h"
#include "SoundWaveProceduralTest.h"
#include "SoundNodeProceduralTest.generated.h"
UCLASS()
class AUDIOTESTPROJECT_API USoundNodeProceduralTest : public USoundNode
{
GENERATED_UCLASS_BODY()
// Volume of the sound [0-1]
UPROPERTY(EditAnywhere, Category="Sound Source Properties")
float Volume;
// Frequency of the test sound [Hz]
UPROPERTY(EditAnywhere, Category="Sound Source Properties")
float Frequency; // [Hz]
USoundWaveProceduralTest *SoundWaveProcedural;
// Begin USoundNode Interface
virtual int32 GetMaxChildNodes() const override;
virtual float GetDuration() override;
virtual void ParseNodes( FAudioDevice* AudioDevice, const UPTRINT NodeWaveInstanceHash, FActiveSound& ActiveSound, const FSoundParseParameters& ParseParams, TArray<FWaveInstance*>& WaveInstances ) override;
virtual FString GetUniqueString() const override;
#if WITH_EDITOR
virtual FString GetTitle() const override;
#endif
protected:
bool SoundWaveIsInitialized;
void CreateSoundWaveStreaming();
};
// EndFile: SoundNodeProceduralTest.h
// BeginFile: SoundNodeProceduralTest.cpp
#include "AudioTestProject.h"
#include "SoundNodeProceduralTest.h"
USoundNodeProceduralTest::USoundNodeProceduralTest(const class FPostConstructInitializeProperties& PCIP)
: Super(PCIP)
, Volume(0.5f)
, Frequency(100.0f)
{
}
int32 USoundNodeProceduralTest::GetMaxChildNodes() const
{
return 0;
}
float USoundNodeProceduralTest::GetDuration()
{
return INDEFINITELY_LOOPING_DURATION;
}
void USoundNodeProceduralTest::ParseNodes(FAudioDevice* AudioDevice, const UPTRINT NodeWaveInstanceHash, FActiveSound& ActiveSound, const FSoundParseParameters& ParseParams, TArray<FWaveInstance*>& WaveInstances)
{
// method 1 : works "sort of" , but seems like the clip stops playing and restarts
if(!SoundWaveIsInitialized)
{
CreateSoundWaveStreaming();
}
if(SoundWaveIsInitialized)
{
if(SoundWaveProcedural)
{
SoundWaveProcedural->Frequency = Frequency;
SoundWaveProcedural->Volume = Volume;
SoundWaveProcedural->Parse(AudioDevice, NodeWaveInstanceHash, ActiveSound, ParseParams, WaveInstances);
}
}
}
void USoundNodeProceduralTest::CreateSoundWaveStreaming()
{
SoundWaveProcedural = NewObject<USoundWaveProceduralTest>();
SoundWaveIsInitialized = true;
}
FString USoundNodeProceduralTest::GetUniqueString() const
{
return TEXT("FOO UNIQUE STRING");
}
#if WITH_EDITOR
FString USoundNodeProceduralTest::GetTitle() const
{
return TEXT("Procedural Sound Wave Source");
}
// EndFile: SoundNodeProceduralTest.cpp
// BeginFile:SoundWaveProceduralTest.h
#pragma once
#include "Sound/SoundWave.h"
#include "SoundWaveProceduralTest.generated.h"
//////////////////////////////////////////////////////////////////////////
/// USoundWaveProceduralTest :
/// A USoundWave that generates a single tone procedurally. This is
/// A test class that demonstrates the ability to create audio
/// procedurally (e.g. other algorithms could be used to generate
/// wind sounds etc.)
//////////////////////////////////////////////////////////////////////////
UCLASS()
class AUDIOTESTPROJECT_API USoundWaveProceduralTest : public USoundWave
{
GENERATED_UCLASS_BODY()
float Frequency; // [Hz]
// UObject interface
virtual void Serialize(FArchive& Ar) override;
virtual void GetAssetRegistryTags(TArray<FAssetRegistryTag>& OutTags) const override;
virtual SIZE_T GetResourceSize(EResourceSizeMode::Type Mode) override;
// USoundWave interface
virtual int32 GeneratePCMData(uint8* PCMData, const int32 SamplesNeeded) override;
virtual int32 GetResourceSizeForFormat(FName Format) override;
virtual FByteBulkData* GetCompressedData(FName Format) override;
virtual void InitAudioResource(FByteBulkData& CompressedData) override;
virtual bool InitAudioResource(FName Format) override;
private:
// Time of the last sample that was copied to the PCMData buffer [s]
float Time;
// Time between two consecutive samples in the audio signal [s]
float DeltaTime;
// Angular frequency of the single tone [rad/s]
float Omega;
};
#endif
// EndFile:SoundWaveProceduralTest.h
// BeginFile:SoundWaveProceduralTest.cpp
#include "AudioTestProject.h"
#include "SoundWaveProceduralTest.h"
USoundWaveProceduralTest::USoundWaveProceduralTest(const class FPostConstructInitializeProperties& PCIP)
: Super(PCIP)
, Time(0.0f)
, Frequency(100.0f)
{
SampleRate = 44100;
NumChannels = 1;
Duration = INDEFINITELY_LOOPING_DURATION;
bProcedural = true;
bLooping = false;
DeltaTime = 1.0f / SampleRate; // time increment between samples [s]
}
int32 USoundWaveProceduralTest::GeneratePCMData(uint8* PCMData, const int32 SamplesNeeded)
{
float TimeStart = Time;
Omega = 2.0f * PI * Frequency; // angular frequency [rad/s]
for(int i = 0; i < SamplesNeeded; i++)
{
Time = TimeStart + i * DeltaTime;
int32 a = FMath::RoundToInt( 65535.0f * (1.0f + FMath::Sin(Omega*Time)) * 0.5f);
if(a > 65535)
{
a = 65535;
}
else if( a < 0)
{
a = 0;
}
uint16 ua = (uint16)a;
PCMData[2*i] = ua; // Low Byte
PCMData[2*i+1] = ua >> 8; // High Byte
}
Time = Time + SamplesNeeded * DeltaTime;
int32 BytesProvided = SamplesNeeded * 2;
return BytesProvided;
}
SIZE_T USoundWaveProceduralTest::GetResourceSize(EResourceSizeMode::Type Mode)
{
return 0;
}
int32 USoundWaveProceduralTest::GetResourceSizeForFormat(FName Format)
{
return 0;
}
void USoundWaveProceduralTest::GetAssetRegistryTags(TArray<FAssetRegistryTag>& OutTags) const
{
// should never be in asset registry
check(false);
}
FByteBulkData* USoundWaveProceduralTest::GetCompressedData(FName Format)
{
return NULL;
}
void USoundWaveProceduralTest::Serialize(FArchive& Ar)
{
// do not call the USoundWave version of serialize
USoundBase::Serialize(Ar);
}
void USoundWaveProceduralTest::InitAudioResource(FByteBulkData& CompressedData)
{
// should never be pushing compressed data
check(false);
}
bool USoundWaveProceduralTest::InitAudioResource(FName Format)
{
//nothing to be done to initialize
return true;
}
// EndFile:SoundWaveProceduralTest.cpp