Files
UnrealEngine/Engine/Source/Runtime/AudioMixer/Public/Components/SynthComponent.h
2025-05-18 13:04:45 +08:00

404 lines
18 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "AudioMixerTypes.h"
#include "Components/AudioComponent.h"
#include "CoreMinimal.h"
#include "Engine/EngineTypes.h"
#include "IAudioExtensionPlugin.h"
#include "Sound/SoundWaveProcedural.h"
#include "Sound/SoundGenerator.h"
#include "UObject/ObjectMacros.h"
#include "SynthComponent.generated.h"
#define SYNTH_GENERATOR_TEST_TONE 0
#if SYNTH_GENERATOR_TEST_TONE
#include "DSP/SinOsc.h"
#endif
class UAudioBus;
class USoundSourceBus;
/** Simple interface class to allow objects to route audio between them. */
class IAudioBufferListener
{
public:
virtual void OnGeneratedBuffer(const float* AudioBuffer, const int32 NumSamples, const int32 NumChannels) = 0;
};
class USynthComponent;
class USoundConcurrency;
/**
* Called by a synth component and returns the sound's envelope value (using an envelope follower in the audio renderer).
* This only works in the audio mixer.
*/
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnSynthEnvelopeValue, const float, EnvelopeValue);
/** shadow delegate declaration for above */
DECLARE_MULTICAST_DELEGATE_TwoParams(FOnSynthEnvelopeValueNative, const class UAudioComponent*, const float);
UCLASS(MinimalAPI)
class USynthSound : public USoundWaveProcedural
{
GENERATED_UCLASS_BODY()
AUDIOMIXER_API void Init(USynthComponent* InSynthComponent, const int32 InNumChannels, const int32 SampleRate, const int32 InCallbackSize);
AUDIOMIXER_API void StartOnAudioDevice(FAudioDevice* InAudioDevice);
/** Begin USoundWave */
AUDIOMIXER_API virtual void OnBeginGenerate() override;
AUDIOMIXER_API virtual int32 OnGeneratePCMAudio(TArray<uint8>& OutAudio, int32 NumSamples) override;
AUDIOMIXER_API virtual void OnEndGenerate() override;
AUDIOMIXER_API virtual Audio::EAudioMixerStreamDataFormat::Type GetGeneratedPCMDataFormat() const override;
AUDIOMIXER_API virtual ISoundGeneratorPtr CreateSoundGenerator(const FSoundGeneratorInitParams& InParams) override;
/** End USoundWave */
protected:
UPROPERTY()
TWeakObjectPtr<USynthComponent> OwningSynthComponent = nullptr;
TArray<float> FloatBuffer;
public:
USynthComponent* GetOwningSynthComponent()
{
return OwningSynthComponent.Get();
}
TWeakObjectPtr<USynthComponent>& GetOwningSynthComponentPtr()
{
return OwningSynthComponent;
}
};
UCLASS(abstract, ClassGroup = Synth, hidecategories = (Object, ActorComponent, Physics, Rendering, Mobility, LOD), MinimalAPI)
class USynthComponent : public USceneComponent
{
GENERATED_BODY()
public:
AUDIOMIXER_API USynthComponent(const FObjectInitializer& ObjectInitializer);
//~ Begin USceneComponent Interface
AUDIOMIXER_API virtual void Activate(bool bReset = false) override;
AUDIOMIXER_API virtual void Deactivate() override;
//~ End USceneComponent Interface
//~ Begin ActorComponent Interface.
AUDIOMIXER_API virtual void OnRegister() override;
AUDIOMIXER_API virtual void OnUnregister() override;
AUDIOMIXER_API virtual bool IsReadyForOwnerToAutoDestroy() const override;
AUDIOMIXER_API virtual void EndPlay(const EEndPlayReason::Type Reason) override;
//~ End ActorComponent Interface.
//~ Begin UObject Interface.
#if WITH_EDITOR
AUDIOMIXER_API virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
#endif // WITH_EDITOR
#if WITH_EDITORONLY_DATA
AUDIOMIXER_API virtual void PostLoad() override;
#endif //WITH_EDITORONLY_DATA
AUDIOMIXER_API virtual void Serialize(FArchive& Ar) override;
//~ End UObject Interface
// Starts the synth generating audio.
UFUNCTION(BlueprintCallable, Category = "Synth|Components|Audio")
AUDIOMIXER_API void Start();
// Stops the synth generating audio.
UFUNCTION(BlueprintCallable, Category = "Synth|Components|Audio")
AUDIOMIXER_API void Stop();
/** Returns true if this component is currently playing. */
UFUNCTION(BlueprintCallable, Category = "Synth|Components|Audio")
AUDIOMIXER_API bool IsPlaying() const;
/** Set a new volume multiplier */
UFUNCTION(BlueprintCallable, Category = "Audio|Components|Audio")
AUDIOMIXER_API void SetVolumeMultiplier(float VolumeMultiplier);
/** Sets how much audio the sound should send to the given submix. */
UFUNCTION(BlueprintCallable, Category = "Audio|Components|Audio")
AUDIOMIXER_API void SetSubmixSend(USoundSubmixBase* Submix, float SendLevel);
/** Sets how much audio the sound should send to the given SourceBus (pre effect). */
UFUNCTION(BlueprintCallable, Category = "Audio|Components|Audio")
AUDIOMIXER_API void SetSourceBusSendPreEffect(USoundSourceBus* SoundSourceBus, float SourceBusSendLevel);
/** Sets how much audio the sound should send to the given SourceBus (post effect). */
UFUNCTION(BlueprintCallable, Category = "Audio|Components|Audio")
AUDIOMIXER_API void SetSourceBusSendPostEffect(USoundSourceBus* SoundSourceBus, float SourceBusSendLevel);
/** Sets how much audio the sound should send to the given AudioBus (pre effect). */
UFUNCTION(BlueprintCallable, Category = "Audio|Components|Audio")
AUDIOMIXER_API void SetAudioBusSendPreEffect(UAudioBus* AudioBus, float AudioBusSendLevel);
/** Sets how much audio the sound should send to the given AudioBus (post effect). */
UFUNCTION(BlueprintCallable, Category = "Audio|Components|Audio")
AUDIOMIXER_API void SetAudioBusSendPostEffect(UAudioBus* AudioBus, float AudioBusSendLevel);
/** Sets whether or not the low pass filter is enabled on the audio component. */
UFUNCTION(BlueprintCallable, Category = "Audio|Components|Audio")
AUDIOMIXER_API void SetLowPassFilterEnabled(bool InLowPassFilterEnabled);
/** Sets lowpass filter frequency of the audio component. */
UFUNCTION(BlueprintCallable, Category = "Audio|Components|Audio")
AUDIOMIXER_API virtual void SetLowPassFilterFrequency(float InLowPassFilterFrequency);
/** Sets whether or not the synth component outputs its audio to any source or audio buses. */
UFUNCTION(BlueprintCallable, Category = "Audio|Components|Audio")
AUDIOMIXER_API void SetOutputToBusOnly(bool bInOutputToBusOnly);
/**
* This function allows designers to call Play on an Audio Component instance while applying a volume curve over time.
* Parameters allow designers to indicate the duration of the fade, the curve shape, and the start time if seeking into the sound.
*
* @param FadeInDuration How long it should take to reach the FadeVolumeLevel
* @param FadeVolumeLevel The percentage of the AudioComponents's calculated volume to fade to
* @param FadeCurve The curve to use when interpolating between the old and new volume
*/
UFUNCTION(BlueprintCallable, Category = "Audio|Components|Audio")
AUDIOMIXER_API void FadeIn(float FadeInDuration, float FadeVolumeLevel = 1.0f, float StartTime = 0.0f, const EAudioFaderCurve FadeCurve = EAudioFaderCurve::Linear) const;
/**
* This function allows designers to call a delayed Stop on an Audio Component instance while applying a
* volume curve over time. Parameters allow designers to indicate the duration of the fade and the curve shape.
*
* @param FadeOutDuration how long it should take to reach the FadeVolumeLevel
* @param FadeVolumeLevel the percentage of the AudioComponents's calculated volume in which to fade to
* @param FadeCurve The curve to use when interpolating between the old and new volume
*/
UFUNCTION(BlueprintCallable, Category = "Audio|Components|Audio")
AUDIOMIXER_API void FadeOut(float FadeOutDuration, float FadeVolumeLevel, const EAudioFaderCurve FadeCurve = EAudioFaderCurve::Linear) const;
/** This function allows designers to trigger an adjustment to the sound instances playback Volume with options for smoothly applying a curve over time.
* @param AdjustVolumeDuration The length of time in which to interpolate between the initial volume and the new volume.
* @param AdjustVolumeLevel The new volume to set the Audio Component to.
* @param FadeCurve The curve used when interpolating between the old and new volume.
*/
UFUNCTION(BlueprintCallable, Category = "Audio|Components|Audio")
AUDIOMIXER_API void AdjustVolume(float AdjustVolumeDuration, float AdjustVolumeLevel, const EAudioFaderCurve FadeCurve = EAudioFaderCurve::Linear) const;
/**
* Sets the routing for one of the given Synth component's Modulation Destinations.
* @param Modulators The set of modulators to apply to the given destination on the component.
* @param Destination The destination to assign the modulators to.
* @param RoutingMethod The routing method to use for the given modulator.
*/
UFUNCTION(BlueprintCallable, Category = "Audio|Components|Audio", DisplayName = "Set Modulation Routing")
AUDIOMIXER_API void SetModulationRouting(const TSet<USoundModulatorBase*>& Modulators, const EModulationDestination Destination, const EModulationRouting RoutingMethod = EModulationRouting::Inherit);
/**
* Gets the set of currently active modulators for a given Modulation Destination.
* @param Destination The Destination to retrieve the Modulators from.
* @return The set of of Modulators applied to this component for the given Destination.
*/
UFUNCTION(BlueprintPure, Category = "Audio|Components|Audio", DisplayName = "Get Modulators")
AUDIOMIXER_API UPARAM(DisplayName = "Modulators") TSet<USoundModulatorBase*> GetModulators(const EModulationDestination Destination);
/** Auto destroy this component on completion */
UPROPERTY()
uint8 bAutoDestroy : 1;
/** Stop sound when owner is destroyed */
UPROPERTY()
uint8 bStopWhenOwnerDestroyed : 1;
/** Is this audio component allowed to be spatialized? */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Attenuation)
uint8 bAllowSpatialization : 1;
/** Should the Attenuation Settings asset be used (false) or should the properties set directly on the component be used for attenuation properties */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Attenuation)
uint8 bOverrideAttenuation : 1;
#if WITH_EDITORONLY_DATA
/** Whether or not to only send this audio's output to a bus. If true, this sound will not be audible except through bus sends. */
UPROPERTY()
uint32 bOutputToBusOnly_DEPRECATED : 1;
#endif //WITH_EDITORONLY_DATA
/** Whether or not to enable sending this audio's output to buses. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Effects)
uint32 bEnableBusSends : 1;
/** If enabled, sound will route to the Master Submix by default or to the Base Submix if defined. If disabled, sound will route ONLY to the Submix Sends and/or Bus Sends */
UPROPERTY(EditAnywhere, Category = Effects)
uint32 bEnableBaseSubmix : 1;
/** Whether or not to enable Submix Sends other than the Base Submix.*/
UPROPERTY(EditAnywhere, Category = Effects, meta = (DisplayAfter = "SoundSubmixObject"))
uint32 bEnableSubmixSends : 1;
/** If bOverrideSettings is false, the asset to use to determine attenuation properties for sounds generated by this component */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Attenuation, meta = (EditCondition = "!bOverrideAttenuation"))
TObjectPtr<class USoundAttenuation> AttenuationSettings;
/** If bOverrideSettings is true, the attenuation properties to use for sounds generated by this component */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Attenuation, meta = (EditCondition = "bOverrideAttenuation"))
struct FSoundAttenuationSettings AttenuationOverrides;
/** What sound concurrency to use for sounds generated by this audio component */
UPROPERTY()
TObjectPtr<USoundConcurrency> ConcurrencySettings_DEPRECATED;
/** What sound concurrency to use for sounds generated by this audio component */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Concurrency)
TSet<TObjectPtr<USoundConcurrency>> ConcurrencySet;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Modulation)
FSoundModulationDefaultRoutingSettings ModulationRouting;
/** Sound class this sound belongs to */
UPROPERTY(EditAnywhere, Category = SoundClass)
TObjectPtr<USoundClass> SoundClass;
/** The source effect chain to use for this sound. */
UPROPERTY(EditAnywhere, Category = Effects)
TObjectPtr<USoundEffectSourcePresetChain> SourceEffectChain;
/** Submix this sound belongs to */
UPROPERTY(EditAnywhere, Category = Effects, meta = (EditCondition = "bEnableBaseSubmix", DisplayName = "Base Submix"))
TObjectPtr<USoundSubmixBase> SoundSubmix;
/** An array of submix sends. Audio from this sound will send a portion of its audio to these effects. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Effects, meta = (EditCondition = "bEnableSubmixSends"))
TArray<FSoundSubmixSendInfo> SoundSubmixSends;
/** This sound will send its audio output to this list of buses if there are bus instances playing after source effects are processed. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Effects, meta = (DisplayName = "Post-Effect Bus Sends", EditCondition = "bEnableBusSends"))
TArray<FSoundSourceBusSendInfo> BusSends;
/** This sound will send its audio output to this list of buses if there are bus instances playing before source effects are processed. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Effects, meta = (DisplayName = "Pre-Effect Bus Sends", EditCondition = "bEnableBusSends"))
TArray<FSoundSourceBusSendInfo> PreEffectBusSends;
/** Whether or not this sound plays when the game is paused in the UI */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Sound)
uint8 bIsUISound : 1;
/** Whether or not this synth is playing as a preview sound */
UPROPERTY()
uint8 bIsPreviewSound : 1;
/** Whether to artificially prioritize the component to play */
uint8 bAlwaysPlay : 1;
/** Call if creating this synth component not via an actor component in BP, but in code or some other location.
* Optionally override the sample rate of the sound wave, otherwise it uses the audio device's sample rate.
*/
AUDIOMIXER_API void Initialize(int32 SampleRateOverride = INDEX_NONE);
/** Creates the audio component if it hasn't already been created yet. This should only be used when trying to
* assign explicit settings to the AudioComponent before calling Start().
*/
AUDIOMIXER_API void CreateAudioComponent();
/** Retrieves this synth component's audio component. */
AUDIOMIXER_API UAudioComponent* GetAudioComponent();
/** The attack time in milliseconds for the envelope follower. Delegate callbacks can be registered to get the
* envelope value of sounds played with this audio component. Only used in audio mixer.
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Sound, meta = (ClampMin = "0", UIMin = "0"))
int32 EnvelopeFollowerAttackTime;
/** The release time in milliseconds for the envelope follower. Delegate callbacks can be registered to get the
* envelope value of sounds played with this audio component. Only used in audio mixer.
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Sound, meta = (ClampMin = "0", UIMin = "0"))
int32 EnvelopeFollowerReleaseTime;
UPROPERTY(BlueprintAssignable)
FOnSynthEnvelopeValue OnAudioEnvelopeValue;
/** Shadow delegate for non UObject subscribers */
FOnSynthEnvelopeValueNative OnAudioEnvelopeValueNative;
AUDIOMIXER_API void OnAudioComponentEnvelopeValue(const UAudioComponent* AudioComponent, const USoundWave* SoundWave, const float EnvelopeValue);
// Adds and removes audio buffer listener
AUDIOMIXER_API void AddAudioBufferListener(IAudioBufferListener* InAudioBufferListener);
AUDIOMIXER_API void RemoveAudioBufferListener(IAudioBufferListener* InAudioBufferListener);
AUDIOMIXER_API virtual USoundClass* GetSoundClass();
AUDIOMIXER_API virtual void BeginDestroy() override;
protected:
// Method to execute parameter changes on game thread in audio render thread
AUDIOMIXER_API void SynthCommand(TFunction<void()> Command);
// Called when synth is created.
virtual bool Init(int32& SampleRate) { return true; }
UE_DEPRECATED(4.26, "Use OnBeginGenerate to get a callback before audio is generating on the audio render thread")
virtual void OnStart() {}
UE_DEPRECATED(4.26, "Use OnEndGenerate to get a callback when audio stops generating on the audio render thread")
virtual void OnStop() {}
// Called when the synth component begins generating audio in render thread
virtual void OnBeginGenerate() {}
// Called when the synth has finished generating audio on the render thread
virtual void OnEndGenerate() {}
// Called when more audio is needed to be generated
// This method of generating audio is soon to be deprecated. For all new synth components, create an FSoundGenerator instance and implement CreateSoundGenerator method to create an instance.
virtual int32 OnGenerateAudio(float* OutAudio, int32 NumSamples) { return 0; }
// Implemented by the synth component to create a generator object instead of generating audio directly on the synth component.
// This method prevents UObjects from having to exist in the audio render thread.
virtual ISoundGeneratorPtr CreateSoundGenerator(const FSoundGeneratorInitParams& InParams) { return nullptr; }
// Called by procedural sound wave
// Returns the number of samples actually generated
AUDIOMIXER_API int32 OnGeneratePCMAudio(float* GeneratedPCMData, int32 NumSamples);
// Gets the audio device associated with this synth component
AUDIOMIXER_API FAudioDevice* GetAudioDevice() const;
// Can be set by the derived class, defaults to 2
int32 NumChannels;
// Can be set by the derived class- sets the preferred callback size for the synth component.
int32 PreferredBufferLength;
private:
// Creates the synth component's sound generator, calls into overridden client code to create the instance.
AUDIOMIXER_API ISoundGeneratorPtr CreateSoundGeneratorInternal(const FSoundGeneratorInitParams& InParams);
UPROPERTY(Transient)
TObjectPtr<USynthSound> Synth;
UPROPERTY(Transient)
TObjectPtr<UAudioComponent> AudioComponent;
AUDIOMIXER_API void PumpPendingMessages();
#if SYNTH_GENERATOR_TEST_TONE
Audio::FSineOsc TestSineLeft;
Audio::FSineOsc TestSineRight;
#endif
// Whether or not synth is playing
bool bIsSynthPlaying;
bool bIsInitialized;
TQueue<TFunction<void()>> CommandQueue;
// Synth component's handle to its sound generator instance.
// used to forward BP functions to the instance directly.
ISoundGeneratorPtr SoundGenerator;
friend class USynthSound;
};