Files
FlaxEngine/Source/Engine/Audio/AudioSource.h
2022-11-23 19:18:30 +01:00

273 lines
9.3 KiB
C++

// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
#pragma once
#include "Engine/Level/Actor.h"
#include "Engine/Content/AssetReference.h"
#include "AudioClip.h"
#include "Config.h"
/// <summary>
/// Represents a source for emitting audio. Audio can be played spatially (gun shot), or normally (music). Each audio source must have an AudioClip to play - back, and it can also have a position in the case of spatial(3D) audio.
/// </summary>
/// <remarks>
/// Whether or not an audio source is spatial is controlled by the assigned AudioClip.The volume and the pitch of a spatial audio source is controlled by its position and the AudioListener's position/direction/velocity.
/// </remarks>
API_CLASS(Attributes="ActorContextMenu(\"New/Audio/Audio Source\"), ActorToolbox(\"Other\")")
class FLAXENGINE_API AudioSource : public Actor
{
DECLARE_SCENE_OBJECT(AudioSource);
friend class AudioStreamingHandler;
friend class AudioClip;
public:
/// <summary>
/// Valid states in which AudioSource can be in.
/// </summary>
API_ENUM() enum class States
{
/// <summary>
/// The source is currently playing.
/// </summary>
Playing = 0,
/// <summary>
/// The source is currently paused (play will resume from paused point).
/// </summary>
Paused = 1,
/// <summary>
/// The source is currently stopped (play will resume from start).
/// </summary>
Stopped = 2
};
private:
Vector3 _velocity;
Vector3 _prevPos;
float _volume;
float _pitch;
float _minDistance;
float _attenuation;
bool _loop;
bool _playOnStart;
bool _isActuallyPlayingSth = false;
bool _needToUpdateStreamingBuffers = false;
States _state = States::Stopped;
States _savedState = States::Stopped;
float _savedTime = 0;
int32 _streamingFirstChunk = 0;
public:
/// <summary>
/// The internal IDs of this audio source used by the audio backend (unique ID per context/listener).
/// </summary>
Array<AUDIO_SOURCE_ID_TYPE, FixedAllocation<AUDIO_MAX_LISTENERS>> SourceIDs;
/// <summary>
/// The audio clip asset used as a source of the sound.
/// </summary>
API_FIELD(Attributes="EditorOrder(10), DefaultValue(null), EditorDisplay(\"Audio Source\")")
AssetReference<AudioClip> Clip;
/// <summary>
/// Gets the velocity of the source. Determines pitch in relation to AudioListener's position. Only relevant for spatial (3D) sources.
/// </summary>
API_PROPERTY() FORCE_INLINE const Vector3& GetVelocity() const
{
return _velocity;
}
/// <summary>
/// Gets the volume of the audio played from this source, in [0, 1] range.
/// </summary>
API_PROPERTY(Attributes="EditorOrder(20), DefaultValue(1.0f), Limit(0, 1, 0.01f), EditorDisplay(\"Audio Source\")")
FORCE_INLINE float GetVolume() const
{
return _volume;
}
/// <summary>
/// Sets the volume of the audio played from this source, in [0, 1] range.
/// </summary>
API_PROPERTY() void SetVolume(float value);
/// <summary>
/// Gets the pitch of the played audio. The default is 1.
/// </summary>
API_PROPERTY(Attributes="EditorOrder(30), DefaultValue(1.0f), Limit(0.5f, 2.0f, 0.01f), EditorDisplay(\"Audio Source\")")
FORCE_INLINE float GetPitch() const
{
return _pitch;
}
/// <summary>
/// Sets the pitch of the played audio. The default is 1.
/// </summary>
API_PROPERTY() void SetPitch(float value);
/// <summary>
/// Determines whether the audio clip should loop when it finishes playing.
/// </summary>
API_PROPERTY(Attributes="EditorOrder(40), DefaultValue(false), EditorDisplay(\"Audio Source\")")
FORCE_INLINE bool GetIsLooping() const
{
return _loop;
}
/// <summary>
/// Determines whether the audio clip should loop when it finishes playing.
/// </summary>
API_PROPERTY() void SetIsLooping(bool value);
/// <summary>
/// Determines whether the audio clip should auto play on level start.
/// </summary>
API_PROPERTY(Attributes="EditorOrder(50), DefaultValue(false), EditorDisplay(\"Audio Source\", \"Play On Start\")")
FORCE_INLINE bool GetPlayOnStart() const
{
return _playOnStart;
}
/// <summary>
/// Determines whether the audio clip should auto play on game start.
/// </summary>
API_PROPERTY() void SetPlayOnStart(bool value);
/// <summary>
/// Gets the minimum distance at which audio attenuation starts. When the listener is closer to the source than this value, audio is heard at full volume. Once farther away the audio starts attenuating.
/// </summary>
/// <returns>The value.</returns>
API_PROPERTY(Attributes="EditorOrder(60), DefaultValue(1.0f), Limit(0, float.MaxValue, 0.1f), EditorDisplay(\"Audio Source\")")
FORCE_INLINE float GetMinDistance() const
{
return _minDistance;
}
/// <summary>
/// Sets the minimum distance at which audio attenuation starts. When the listener is closer to the source than this value, audio is heard at full volume. Once farther away the audio starts attenuating.
/// </summary>
API_PROPERTY() void SetMinDistance(float value);
/// <summary>
/// Gets the attenuation that controls how quickly does audio volume drop off as the listener moves further from the source.
/// </summary>
API_PROPERTY(Attributes="EditorOrder(70), DefaultValue(1.0f), Limit(0, float.MaxValue, 0.1f), EditorDisplay(\"Audio Source\")")
FORCE_INLINE float GetAttenuation() const
{
return _attenuation;
}
/// <summary>
/// Sets the attenuation that controls how quickly does audio volume drop off as the listener moves further from the source.
/// </summary>
API_PROPERTY() void SetAttenuation(float value);
public:
/// <summary>
/// Starts playing the currently assigned audio clip.
/// </summary>
API_FUNCTION() void Play();
/// <summary>
/// Pauses the audio playback.
/// </summary>
API_FUNCTION() void Pause();
/// <summary>
/// Stops audio playback, rewinding it to the start.
/// </summary>
API_FUNCTION() void Stop();
/// <summary>
/// Gets the current state of the audio playback (playing/paused/stopped).
/// </summary>
API_PROPERTY() FORCE_INLINE AudioSource::States GetState() const
{
return _state;
}
/// <summary>
/// Gets the current time of playback. If playback has not yet started, it specifies the time at which playback will start at. The time is in seconds, in range [0, ClipLength].
/// </summary>
API_PROPERTY(Attributes="HideInEditor") float GetTime() const;
/// <summary>
/// Sets the current time of playback. If playback has not yet started, it specifies the time at which playback will start at. The time is in seconds, in range [0, ClipLength].
/// </summary>
/// <param name="time">The time.</param>
API_PROPERTY() void SetTime(float time);
/// <summary>
/// Returns true if the sound source is three dimensional (volume and pitch varies based on listener distance and velocity).
/// </summary>
API_PROPERTY() bool Is3D() const;
/// <summary>
/// Returns true if audio clip is valid, loaded and uses dynamic data streaming.
/// </summary>
API_PROPERTY() bool UseStreaming() const;
/// <summary>
/// Restores the saved time position and resumes/pauses the playback based on the state before. Used to restore audio source state after data rebuild (eg. by audio backend).
/// </summary>
void Restore();
public:
/// <summary>
/// Determines whether this audio source started playing audio via audio backend. After audio play it may wait for audio clip data to be loaded or streamed.
/// </summary>
API_PROPERTY() FORCE_INLINE bool IsActuallyPlayingSth() const
{
return _isActuallyPlayingSth;
}
/// <summary>
/// Requests the audio streaming buffers update. Rises tha flag to synchronize audio backend buffers of the emitter during next game logic update.
/// </summary>
void RequestStreamingBuffersUpdate();
/// <summary>
/// Cleanups the cached data. Called by the Audio manager.
/// </summary>
void Cleanup();
private:
void OnClipChanged();
void OnClipLoaded();
/// <summary>
/// Sets the single buffer from the audio clip that is not using dynamic streaming
/// </summary>
void SetNonStreamingBuffer();
/// <summary>
/// Plays the audio source. Should have buffer(s) binded before.
/// </summary>
void PlayInternal();
void Update();
public:
// [Actor]
#if USE_EDITOR
BoundingBox GetEditorBox() const override
{
const Vector3 size(50);
return BoundingBox(_transform.Translation - size, _transform.Translation + size);
}
#endif
void Serialize(SerializeStream& stream, const void* otherObj) override;
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;
bool HasContentLoaded() const override;
bool IntersectsItself(const Ray& ray, Real& distance, Vector3& normal) override;
protected:
// [Actor]
void OnEnable() override;
void OnDisable() override;
void OnTransformChanged() override;
void BeginPlay(SceneBeginData* data) override;
};