Refactor Audio Backend to not depend on AudioSource object

This commit is contained in:
Wojtek Figat
2024-05-06 10:36:36 +02:00
parent 5b2af6b3d5
commit f43cd97907
14 changed files with 532 additions and 627 deletions

View File

@@ -2,7 +2,6 @@
#include "Audio.h"
#include "AudioBackend.h"
#include "AudioListener.h"
#include "AudioSettings.h"
#include "FlaxEngine.Gen.h"
#include "Engine/Scripting/ScriptingType.h"
@@ -149,45 +148,6 @@ void Audio::SetEnableHRTF(bool value)
AudioBackend::Listener::ReinitializeAll();
}
void Audio::OnAddListener(AudioListener* listener)
{
ASSERT(!Listeners.Contains(listener));
if (Listeners.Count() >= AUDIO_MAX_LISTENERS)
{
LOG(Error, "Unsupported amount of the audio listeners!");
return;
}
Listeners.Add(listener);
AudioBackend::Listener::Reset();
AudioBackend::Listener::TransformChanged(listener->GetPosition(), listener->GetOrientation());
}
void Audio::OnRemoveListener(AudioListener* listener)
{
if (!Listeners.Remove(listener))
{
AudioBackend::Listener::Reset();
}
}
void Audio::OnAddSource(AudioSource* source)
{
ASSERT(!Sources.Contains(source));
Sources.Add(source);
AudioBackend::Source::OnAdd(source);
}
void Audio::OnRemoveSource(AudioSource* source)
{
if (!Sources.Remove(source))
{
AudioBackend::Source::OnRemove(source);
}
}
bool AudioService::Init()
{
PROFILE_CPU_NAMED("Audio.Init");

View File

@@ -97,11 +97,4 @@ public:
/// </summary>
/// <param name="value">The value.</param>
API_PROPERTY() static void SetEnableHRTF(bool value);
public:
static void OnAddListener(AudioListener* listener);
static void OnRemoveListener(AudioListener* listener);
static void OnAddSource(AudioSource* source);
static void OnRemoveSource(AudioSource* source);
};

View File

@@ -32,32 +32,30 @@ private:
virtual void Listener_ReinitializeAll() = 0;
// Source
virtual void Source_OnAdd(AudioSource* source) = 0;
virtual void Source_OnRemove(AudioSource* source) = 0;
virtual void Source_VelocityChanged(AudioSource* source) = 0;
virtual void Source_TransformChanged(AudioSource* source) = 0;
virtual void Source_VolumeChanged(AudioSource* source) = 0;
virtual void Source_PitchChanged(AudioSource* source) = 0;
virtual void Source_PanChanged(AudioSource* source) = 0;
virtual void Source_IsLoopingChanged(AudioSource* source) = 0;
virtual void Source_SpatialSetupChanged(AudioSource* source) = 0;
virtual void Source_ClipLoaded(AudioSource* source) = 0;
virtual void Source_Cleanup(AudioSource* source) = 0;
virtual void Source_Play(AudioSource* source) = 0;
virtual void Source_Pause(AudioSource* source) = 0;
virtual void Source_Stop(AudioSource* source) = 0;
virtual void Source_SetCurrentBufferTime(AudioSource* source, float value) = 0;
virtual float Source_GetCurrentBufferTime(const AudioSource* source) = 0;
virtual void Source_SetNonStreamingBuffer(AudioSource* source) = 0;
virtual void Source_GetProcessedBuffersCount(AudioSource* source, int32& processedBuffersCount) = 0;
virtual void Source_GetQueuedBuffersCount(AudioSource* source, int32& queuedBuffersCount) = 0;
virtual void Source_QueueBuffer(AudioSource* source, uint32 bufferId) = 0;
virtual void Source_DequeueProcessedBuffers(AudioSource* source) = 0;
virtual uint32 Source_Add(const AudioDataInfo& format, const Vector3& position, const Quaternion& orientation, float volume, float pitch, float pan, bool loop, bool spatial, float attenuation, float minDistance, float doppler) = 0;
virtual void Source_Remove(uint32 sourceID) = 0;
virtual void Source_VelocityChanged(uint32 sourceID, const Vector3& velocity) = 0;
virtual void Source_TransformChanged(uint32 sourceID, const Vector3& position, const Quaternion& orientation) = 0;
virtual void Source_VolumeChanged(uint32 sourceID, float volume) = 0;
virtual void Source_PitchChanged(uint32 sourceID, float pitch) = 0;
virtual void Source_PanChanged(uint32 sourceID, float pan) = 0;
virtual void Source_IsLoopingChanged(uint32 sourceID, bool loop) = 0;
virtual void Source_SpatialSetupChanged(uint32 sourceID, bool spatial, float attenuation, float minDistance, float doppler) = 0;
virtual void Source_Play(uint32 sourceID) = 0;
virtual void Source_Pause(uint32 sourceID) = 0;
virtual void Source_Stop(uint32 sourceID) = 0;
virtual void Source_SetCurrentBufferTime(uint32 sourceID, float value) = 0;
virtual float Source_GetCurrentBufferTime(uint32 id) = 0;
virtual void Source_SetNonStreamingBuffer(uint32 sourceID, uint32 bufferID) = 0;
virtual void Source_GetProcessedBuffersCount(uint32 sourceID, int32& processedBuffersCount) = 0;
virtual void Source_GetQueuedBuffersCount(uint32 sourceID, int32& queuedBuffersCount) = 0;
virtual void Source_QueueBuffer(uint32 sourceID, uint32 bufferID) = 0;
virtual void Source_DequeueProcessedBuffers(uint32 sourceID) = 0;
// Buffer
virtual uint32 Buffer_Create() = 0;
virtual void Buffer_Delete(uint32 bufferId) = 0;
virtual void Buffer_Write(uint32 bufferId, byte* samples, const AudioDataInfo& info) = 0;
virtual void Buffer_Delete(uint32 bufferID) = 0;
virtual void Buffer_Write(uint32 bufferID, byte* samples, const AudioDataInfo& info) = 0;
// Base
virtual const Char* Base_Name() = 0;
@@ -102,109 +100,99 @@ public:
class Source
{
public:
FORCE_INLINE static void OnAdd(AudioSource* source)
FORCE_INLINE static uint32 Add(const AudioDataInfo& format, const Vector3& position, const Quaternion& orientation, float volume, float pitch, float pan, bool loop, bool spatial, float attenuation, float minDistance, float doppler)
{
Instance->Source_OnAdd(source);
return Instance->Source_Add(format, position, orientation, volume, pitch, pan, loop, spatial, attenuation, minDistance, doppler);
}
FORCE_INLINE static void OnRemove(AudioSource* source)
FORCE_INLINE static void Remove(uint32 sourceID)
{
Instance->Source_OnRemove(source);
Instance->Source_Remove(sourceID);
}
FORCE_INLINE static void VelocityChanged(AudioSource* source)
FORCE_INLINE static void VelocityChanged(uint32 sourceID, const Vector3& velocity)
{
Instance->Source_VelocityChanged(source);
Instance->Source_VelocityChanged(sourceID, velocity);
}
FORCE_INLINE static void TransformChanged(AudioSource* source)
FORCE_INLINE static void TransformChanged(uint32 sourceID, const Vector3& position, const Quaternion& orientation)
{
Instance->Source_TransformChanged(source);
Instance->Source_TransformChanged(sourceID, position, orientation);
}
FORCE_INLINE static void VolumeChanged(AudioSource* source)
FORCE_INLINE static void VolumeChanged(uint32 sourceID, float volume)
{
Instance->Source_VolumeChanged(source);
Instance->Source_VolumeChanged(sourceID, volume);
}
FORCE_INLINE static void PitchChanged(AudioSource* source)
FORCE_INLINE static void PitchChanged(uint32 sourceID, float pitch)
{
Instance->Source_PitchChanged(source);
Instance->Source_PitchChanged(sourceID, pitch);
}
FORCE_INLINE static void PanChanged(AudioSource* source)
FORCE_INLINE static void PanChanged(uint32 sourceID, float pan)
{
Instance->Source_PanChanged(source);
Instance->Source_PanChanged(sourceID, pan);
}
FORCE_INLINE static void IsLoopingChanged(AudioSource* source)
FORCE_INLINE static void IsLoopingChanged(uint32 sourceID, bool loop)
{
Instance->Source_IsLoopingChanged(source);
Instance->Source_IsLoopingChanged(sourceID, loop);
}
FORCE_INLINE static void SpatialSetupChanged(AudioSource* source)
FORCE_INLINE static void SpatialSetupChanged(uint32 sourceID, bool spatial, float attenuation, float minDistance, float doppler)
{
Instance->Source_SpatialSetupChanged(source);
Instance->Source_SpatialSetupChanged(sourceID, spatial, attenuation, minDistance, doppler);
}
FORCE_INLINE static void ClipLoaded(AudioSource* source)
FORCE_INLINE static void Play(uint32 sourceID)
{
Instance->Source_ClipLoaded(source);
Instance->Source_Play(sourceID);
}
FORCE_INLINE static void Cleanup(AudioSource* source)
FORCE_INLINE static void Pause(uint32 sourceID)
{
Instance->Source_Cleanup(source);
Instance->Source_Pause(sourceID);
}
FORCE_INLINE static void Play(AudioSource* source)
FORCE_INLINE static void Stop(uint32 sourceID)
{
Instance->Source_Play(source);
Instance->Source_Stop(sourceID);
}
FORCE_INLINE static void Pause(AudioSource* source)
FORCE_INLINE static void SetCurrentBufferTime(uint32 sourceID, float value)
{
Instance->Source_Pause(source);
Instance->Source_SetCurrentBufferTime(sourceID, value);
}
FORCE_INLINE static void Stop(AudioSource* source)
FORCE_INLINE static float GetCurrentBufferTime(uint32 sourceID)
{
Instance->Source_Stop(source);
return Instance->Source_GetCurrentBufferTime(sourceID);
}
FORCE_INLINE static void SetCurrentBufferTime(AudioSource* source, float value)
FORCE_INLINE static void SetNonStreamingBuffer(uint32 sourceID, uint32 bufferID)
{
Instance->Source_SetCurrentBufferTime(source, value);
Instance->Source_SetNonStreamingBuffer(sourceID, bufferID);
}
FORCE_INLINE static float GetCurrentBufferTime(const AudioSource* source)
FORCE_INLINE static void GetProcessedBuffersCount(uint32 sourceID, int32& processedBuffersCount)
{
return Instance->Source_GetCurrentBufferTime(source);
Instance->Source_GetProcessedBuffersCount(sourceID, processedBuffersCount);
}
FORCE_INLINE static void SetNonStreamingBuffer(AudioSource* source)
FORCE_INLINE static void GetQueuedBuffersCount(uint32 sourceID, int32& queuedBuffersCount)
{
Instance->Source_SetNonStreamingBuffer(source);
Instance->Source_GetQueuedBuffersCount(sourceID, queuedBuffersCount);
}
FORCE_INLINE static void GetProcessedBuffersCount(AudioSource* source, int32& processedBuffersCount)
FORCE_INLINE static void QueueBuffer(uint32 sourceID, uint32 bufferID)
{
Instance->Source_GetProcessedBuffersCount(source, processedBuffersCount);
Instance->Source_QueueBuffer(sourceID, bufferID);
}
FORCE_INLINE static void GetQueuedBuffersCount(AudioSource* source, int32& queuedBuffersCount)
FORCE_INLINE static void DequeueProcessedBuffers(uint32 sourceID)
{
Instance->Source_GetQueuedBuffersCount(source, queuedBuffersCount);
}
FORCE_INLINE static void QueueBuffer(AudioSource* source, uint32 bufferId)
{
Instance->Source_QueueBuffer(source, bufferId);
}
FORCE_INLINE static void DequeueProcessedBuffers(AudioSource* source)
{
Instance->Source_DequeueProcessedBuffers(source);
Instance->Source_DequeueProcessedBuffers(sourceID);
}
};
@@ -216,14 +204,14 @@ public:
return Instance->Buffer_Create();
}
FORCE_INLINE static void Delete(uint32 bufferId)
FORCE_INLINE static void Delete(uint32 bufferID)
{
Instance->Buffer_Delete(bufferId);
Instance->Buffer_Delete(bufferID);
}
FORCE_INLINE static void Write(uint32 bufferId, byte* samples, const AudioDataInfo& info)
FORCE_INLINE static void Write(uint32 bufferID, byte* samples, const AudioDataInfo& info)
{
Instance->Buffer_Write(bufferId, samples, info);
Instance->Buffer_Write(bufferID, samples, info);
}
};

View File

@@ -31,16 +31,16 @@ bool AudioClip::StreamingTask::Run()
for (int32 i = 0; i < queue.Count(); i++)
{
const auto idx = queue[i];
uint32& bufferId = clip->Buffers[idx];
if (bufferId == 0)
uint32& bufferID = clip->Buffers[idx];
if (bufferID == 0)
{
bufferId = AudioBackend::Buffer::Create();
bufferID = AudioBackend::Buffer::Create();
}
else
{
// Release unused data
AudioBackend::Buffer::Delete(bufferId);
bufferId = 0;
AudioBackend::Buffer::Delete(bufferID);
bufferID = 0;
}
}
@@ -383,8 +383,8 @@ Asset::LoadResult AudioClip::load()
void AudioClip::unload(bool isReloading)
{
bool hasAnyBuffer = false;
for (const uint32 bufferId : Buffers)
hasAnyBuffer |= bufferId != 0;
for (const uint32 bufferID : Buffers)
hasAnyBuffer |= bufferID != 0;
// Stop any audio sources that are using this clip right now
// TODO: find better way to collect audio sources using audio clip and impl it for AudioStreamingHandler too
@@ -399,10 +399,10 @@ void AudioClip::unload(bool isReloading)
StreamingQueue.Clear();
if (hasAnyBuffer && AudioBackend::Instance)
{
for (uint32 bufferId : Buffers)
for (uint32 bufferID : Buffers)
{
if (bufferId != 0)
AudioBackend::Buffer::Delete(bufferId);
if (bufferID != 0)
AudioBackend::Buffer::Delete(bufferID);
}
}
Buffers.Clear();
@@ -413,8 +413,8 @@ void AudioClip::unload(bool isReloading)
bool AudioClip::WriteBuffer(int32 chunkIndex)
{
// Ignore if buffer is not created
const uint32 bufferId = Buffers[chunkIndex];
if (bufferId == 0)
const uint32 bufferID = Buffers[chunkIndex];
if (bufferID == 0)
return false;
// Ensure audio backend exists
@@ -475,6 +475,6 @@ bool AudioClip::WriteBuffer(int32 chunkIndex)
}
// Write samples to the audio buffer
AudioBackend::Buffer::Write(bufferId, data.Get(), info);
AudioBackend::Buffer::Write(bufferID, data.Get(), info);
return false;
}

View File

@@ -3,6 +3,7 @@
#include "AudioListener.h"
#include "Engine/Engine/Time.h"
#include "Engine/Level/Scene/Scene.h"
#include "Engine/Core/Log.h"
#include "AudioBackend.h"
#include "Audio.h"
@@ -36,9 +37,18 @@ void AudioListener::OnEnable()
{
_prevPos = GetPosition();
_velocity = Vector3::Zero;
Audio::OnAddListener(this);
GetScene()->Ticking.Update.AddTick<AudioListener, &AudioListener::Update>(this);
if (Audio::Listeners.Count() >= AUDIO_MAX_LISTENERS)
{
LOG(Error, "Unsupported amount of the audio listeners!");
}
else
{
ASSERT(!Audio::Listeners.Contains(this));
Audio::Listeners.Add(this);
AudioBackend::Listener::Reset();
AudioBackend::Listener::TransformChanged(GetPosition(), GetOrientation());
GetScene()->Ticking.Update.AddTick<AudioListener, &AudioListener::Update>(this);
}
#if USE_EDITOR
GetSceneRendering()->AddViewportIcon(this);
#endif
@@ -52,8 +62,11 @@ void AudioListener::OnDisable()
#if USE_EDITOR
GetSceneRendering()->RemoveViewportIcon(this);
#endif
GetScene()->Ticking.Update.RemoveTick(this);
Audio::OnRemoveListener(this);
if (!Audio::Listeners.Remove(this))
{
GetScene()->Ticking.Update.RemoveTick(this);
AudioBackend::Listener::Reset();
}
// Base
Actor::OnDisable();

View File

@@ -33,7 +33,7 @@ void AudioSource::SetVolume(float value)
return;
_volume = value;
if (SourceID)
AudioBackend::Source::VolumeChanged(this);
AudioBackend::Source::VolumeChanged(SourceID, _volume);
}
void AudioSource::SetPitch(float value)
@@ -43,7 +43,7 @@ void AudioSource::SetPitch(float value)
return;
_pitch = value;
if (SourceID)
AudioBackend::Source::PitchChanged(this);
AudioBackend::Source::PitchChanged(SourceID, _pitch);
}
void AudioSource::SetPan(float value)
@@ -53,7 +53,7 @@ void AudioSource::SetPan(float value)
return;
_pan = value;
if (SourceID)
AudioBackend::Source::PanChanged(this);
AudioBackend::Source::PanChanged(SourceID, _pan);
}
void AudioSource::SetIsLooping(bool value)
@@ -64,7 +64,7 @@ void AudioSource::SetIsLooping(bool value)
// When streaming we handle looping manually by the proper buffers submission
if (SourceID && !UseStreaming())
AudioBackend::Source::IsLoopingChanged(this);
AudioBackend::Source::IsLoopingChanged(SourceID, _loop);
}
void AudioSource::SetPlayOnStart(bool value)
@@ -84,7 +84,7 @@ void AudioSource::SetMinDistance(float value)
return;
_minDistance = value;
if (SourceID)
AudioBackend::Source::SpatialSetupChanged(this);
AudioBackend::Source::SpatialSetupChanged(SourceID, Is3D(), _attenuation, _minDistance, _dopplerFactor);
}
void AudioSource::SetAttenuation(float value)
@@ -94,7 +94,7 @@ void AudioSource::SetAttenuation(float value)
return;
_attenuation = value;
if (SourceID)
AudioBackend::Source::SpatialSetupChanged(this);
AudioBackend::Source::SpatialSetupChanged(SourceID, Is3D(), _attenuation, _minDistance, _dopplerFactor);
}
void AudioSource::SetDopplerFactor(float value)
@@ -104,7 +104,7 @@ void AudioSource::SetDopplerFactor(float value)
return;
_dopplerFactor = value;
if (SourceID)
AudioBackend::Source::SpatialSetupChanged(this);
AudioBackend::Source::SpatialSetupChanged(SourceID, Is3D(), _attenuation, _minDistance, _dopplerFactor);
}
void AudioSource::SetAllowSpatialization(bool value)
@@ -113,7 +113,7 @@ void AudioSource::SetAllowSpatialization(bool value)
return;
_allowSpatialization = value;
if (SourceID)
AudioBackend::Source::SpatialSetupChanged(this);
AudioBackend::Source::SpatialSetupChanged(SourceID, Is3D(), _attenuation, _minDistance, _dopplerFactor);
}
void AudioSource::Play()
@@ -121,19 +121,26 @@ void AudioSource::Play()
auto state = _state;
if (state == States::Playing)
return;
if (Clip == nullptr)
if (Clip == nullptr || Clip->WaitForLoaded())
{
LOG(Warning, "Cannot play audio source without a clip ({0})", GetNamePath());
return;
}
if (SourceID == 0)
{
// Create audio source
SourceID = AudioBackend::Source::Add(Clip->Info(), GetPosition(), GetOrientation(), GetVolume(), GetPitch(), GetPan(), GetIsLooping() && !UseStreaming(), Is3D(), GetAttenuation(), GetMinDistance(), GetDopplerFactor());
if (SourceID == 0)
{
LOG(Warning, "Cannot create audio source ({0})", GetNamePath());
return;
}
}
_state = States::Playing;
_isActuallyPlayingSth = false;
// Don't block scripting if audio is not loaded or has missing streaming data
if (!Clip->IsLoaded())
return;
// Audio clips with disabled streaming are controlled by audio source, otherwise streaming manager will play it
if (Clip->IsStreamable())
{
@@ -155,7 +162,7 @@ void AudioSource::Play()
else if (SourceID)
{
// Play it right away
SetNonStreamingBuffer();
AudioBackend::Source::SetNonStreamingBuffer(SourceID, Clip->Buffers[0]);
PlayInternal();
}
else
@@ -171,10 +178,9 @@ void AudioSource::Pause()
return;
_state = States::Paused;
if (_isActuallyPlayingSth)
{
AudioBackend::Source::Pause(this);
AudioBackend::Source::Pause(SourceID);
_isActuallyPlayingSth = false;
}
}
@@ -188,7 +194,7 @@ void AudioSource::Stop()
_isActuallyPlayingSth = false;
_streamingFirstChunk = 0;
if (SourceID)
AudioBackend::Source::Stop(this);
AudioBackend::Source::Stop(SourceID);
}
float AudioSource::GetTime() const
@@ -196,13 +202,13 @@ float AudioSource::GetTime() const
if (_state == States::Stopped || SourceID == 0 || !Clip->IsLoaded())
return 0.0f;
float time = AudioBackend::Source::GetCurrentBufferTime(this);
float time = AudioBackend::Source::GetCurrentBufferTime(SourceID);
if (UseStreaming())
{
// Apply time offset to the first streaming buffer binded to the source including the already queued buffers
int32 numProcessedBuffers = 0;
AudioBackend::Source::GetProcessedBuffersCount(const_cast<AudioSource*>(this), numProcessedBuffers);
AudioBackend::Source::GetProcessedBuffersCount(SourceID, numProcessedBuffers);
time += Clip->GetBufferStartTime(_streamingFirstChunk + numProcessedBuffers);
}
@@ -234,7 +240,7 @@ void AudioSource::SetTime(float time)
time = relativeTime;
}
AudioBackend::Source::SetCurrentBufferTime(this, time);
AudioBackend::Source::SetCurrentBufferTime(SourceID, time);
// Restore state if was stopped
if (isActuallyPlayingSth)
@@ -258,31 +264,29 @@ void AudioSource::RequestStreamingBuffersUpdate()
_needToUpdateStreamingBuffers = true;
}
void AudioSource::Cleanup()
void AudioSource::OnClipChanged()
{
_savedState = GetState();
_savedTime = GetTime();
Stop();
// Destroy current source (will be created on the next play), because clip might use different spatial options or audio data format
if (SourceID)
{
AudioBackend::Source::Cleanup(this);
AudioBackend::Source::Remove(SourceID);
SourceID = 0;
}
}
void AudioSource::OnClipChanged()
{
Stop();
_clipChanged = true;
}
void AudioSource::OnClipLoaded()
{
AudioBackend::Source::ClipLoaded(this);
if (!SourceID)
return;
// Reset spatial and playback
AudioBackend::Source::IsLoopingChanged(SourceID, _loop && !UseStreaming());
AudioBackend::Source::SpatialSetupChanged(SourceID, Is3D(), _attenuation, _minDistance, _dopplerFactor);
// Start playing if source was waiting for the clip to load
if (SourceID && _state == States::Playing && !_isActuallyPlayingSth)
if (_state == States::Playing && !_isActuallyPlayingSth)
{
if (Clip->IsStreamable())
{
@@ -292,7 +296,7 @@ void AudioSource::OnClipLoaded()
else
{
// Play it right away
SetNonStreamingBuffer();
AudioBackend::Source::SetNonStreamingBuffer(SourceID, Clip->Buffers[0]);
PlayInternal();
}
}
@@ -300,42 +304,14 @@ void AudioSource::OnClipLoaded()
bool AudioSource::UseStreaming() const
{
return Clip && Clip->IsLoaded() && Clip->IsStreamable();
}
void AudioSource::Restore()
{
if (Clip)
{
if (_savedState != States::Stopped)
Play();
if (_savedState == States::Paused)
Pause();
SetTime(_savedTime);
if (_savedState != States::Stopped && UseStreaming())
RequestStreamingBuffersUpdate();
}
}
void AudioSource::SetNonStreamingBuffer()
{
ASSERT(Clip && !Clip->IsStreamable());
AudioBackend::Source::SetNonStreamingBuffer(this);
if (Clip == nullptr || Clip->WaitForLoaded())
return false;
return Clip->IsStreamable();
}
void AudioSource::PlayInternal()
{
if (_clipChanged && SourceID != 0)
{
// If clip was changed between source setup (OnEnable) and actual playback start then ensure to flush any runtime properties with the audio backend
_clipChanged = false;
AudioBackend::Source::SpatialSetupChanged(this);
}
AudioBackend::Source::Play(this);
AudioBackend::Source::Play(SourceID);
_isActuallyPlayingSth = true;
}
@@ -413,9 +389,9 @@ void AudioSource::Update()
const auto prevVelocity = _velocity;
_velocity = (pos - _prevPos) / dt;
_prevPos = pos;
if (_velocity != prevVelocity)
if (_velocity != prevVelocity && Is3D())
{
AudioBackend::Source::VelocityChanged(this);
AudioBackend::Source::VelocityChanged(SourceID, _velocity);
}
// Skip other update logic if it's not valid streamable source
@@ -429,17 +405,17 @@ void AudioSource::Update()
{
// Get buffers in a queue count
int32 numQueuedBuffers;
AudioBackend::Source::GetQueuedBuffersCount(this, numQueuedBuffers);
AudioBackend::Source::GetQueuedBuffersCount(SourceID, numQueuedBuffers);
// Queue missing buffers
uint32 bufferId;
if (numQueuedBuffers < 1 && (bufferId = clip->Buffers[_streamingFirstChunk]) != 0)
uint32 bufferID;
if (numQueuedBuffers < 1 && (bufferID = clip->Buffers[_streamingFirstChunk]) != 0)
{
AudioBackend::Source::QueueBuffer(this, bufferId);
AudioBackend::Source::QueueBuffer(SourceID, bufferID);
}
if (numQueuedBuffers < 2 && _streamingFirstChunk + 1 < clip->Buffers.Count() && (bufferId = clip->Buffers[_streamingFirstChunk + 1]) != 0)
if (numQueuedBuffers < 2 && _streamingFirstChunk + 1 < clip->Buffers.Count() && (bufferID = clip->Buffers[_streamingFirstChunk + 1]) != 0)
{
AudioBackend::Source::QueueBuffer(this, bufferId);
AudioBackend::Source::QueueBuffer(SourceID, bufferID);
}
// Clear flag
@@ -457,13 +433,13 @@ void AudioSource::Update()
{
// Get the processed buffers count
int32 numProcessedBuffers = 0;
AudioBackend::Source::GetProcessedBuffersCount(this, numProcessedBuffers);
AudioBackend::Source::GetProcessedBuffersCount(SourceID, numProcessedBuffers);
if (numProcessedBuffers > 0)
{
ASSERT(numProcessedBuffers <= ASSET_FILE_DATA_CHUNKS);
// Unbind processed buffers from the source
AudioBackend::Source::DequeueProcessedBuffers(this);
AudioBackend::Source::DequeueProcessedBuffers(SourceID);
// Move the chunk pointer (AudioStreamingHandler will request new chunks streaming)
_streamingFirstChunk += numProcessedBuffers;
@@ -500,27 +476,53 @@ void AudioSource::OnEnable()
{
_prevPos = GetPosition();
_velocity = Vector3::Zero;
_clipChanged = false;
Audio::OnAddSource(this);
// Add source
ASSERT_LOW_LAYER(!Audio::Sources.Contains(this));
Audio::Sources.Add(this);
GetScene()->Ticking.Update.AddTick<AudioSource, &AudioSource::Update>(this);
#if USE_EDITOR
GetSceneRendering()->AddViewportIcon(this);
#endif
// Restore playback state
if (Clip)
{
if (_savedState != States::Stopped)
Play();
if (_savedState == States::Paused)
Pause();
SetTime(_savedTime);
if (_savedState != States::Stopped && UseStreaming())
RequestStreamingBuffersUpdate();
}
// Base
Actor::OnEnable();
}
void AudioSource::OnDisable()
{
// Cache playback state
_savedState = GetState();
_savedTime = GetTime();
// End playback
Stop();
// Remove source
#if USE_EDITOR
GetSceneRendering()->RemoveViewportIcon(this);
#endif
GetScene()->Ticking.Update.RemoveTick(this);
Audio::OnRemoveSource(this);
if (SourceID)
{
AudioBackend::Source::Remove(SourceID);
SourceID = 0;
}
Audio::Sources.Remove(this);
// Base
Actor::OnDisable();
@@ -534,9 +536,9 @@ void AudioSource::OnTransformChanged()
_box = BoundingBox(_transform.Translation);
_sphere = BoundingSphere(_transform.Translation, 0.0f);
if (IsActiveInHierarchy() && SourceID)
if (IsActiveInHierarchy() && SourceID && Is3D())
{
AudioBackend::Source::TransformChanged(this);
AudioBackend::Source::TransformChanged(SourceID, _transform.Translation, _transform.Orientation);
}
}

View File

@@ -55,7 +55,6 @@ private:
bool _playOnStart;
float _startTime;
bool _allowSpatialization;
bool _clipChanged = false;
bool _isActuallyPlayingSth = false;
bool _needToUpdateStreamingBuffers = false;
@@ -270,11 +269,6 @@ public:
/// </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.
@@ -289,20 +283,10 @@ public:
/// </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>

View File

@@ -3,6 +3,7 @@
#pragma once
#include "Engine/Core/Config.h"
#include "Engine/Content/Config.h"
// The maximum amount of listeners used at once
#define AUDIO_MAX_LISTENERS 1

View File

@@ -22,91 +22,82 @@ void AudioBackendNone::Listener_ReinitializeAll()
{
}
void AudioBackendNone::Source_OnAdd(AudioSource* source)
uint32 AudioBackendNone::Source_Add(const AudioDataInfo& format, const Vector3& position, const Quaternion& orientation, float volume, float pitch, float pan, bool loop, bool spatial, float attenuation, float minDistance, float doppler)
{
source->Restore();
return 1;
}
void AudioBackendNone::Source_OnRemove(AudioSource* source)
{
source->Cleanup();
}
void AudioBackendNone::Source_VelocityChanged(AudioSource* source)
void AudioBackendNone::Source_Remove(uint32 sourceID)
{
}
void AudioBackendNone::Source_TransformChanged(AudioSource* source)
void AudioBackendNone::Source_VelocityChanged(uint32 sourceID, const Vector3& velocity)
{
}
void AudioBackendNone::Source_VolumeChanged(AudioSource* source)
void AudioBackendNone::Source_TransformChanged(uint32 sourceID, const Vector3& position, const Quaternion& orientation)
{
}
void AudioBackendNone::Source_PitchChanged(AudioSource* source)
void AudioBackendNone::Source_VolumeChanged(uint32 sourceID, float volume)
{
}
void AudioBackendNone::Source_PanChanged(AudioSource* source)
void AudioBackendNone::Source_PitchChanged(uint32 sourceID, float pitch)
{
}
void AudioBackendNone::Source_IsLoopingChanged(AudioSource* source)
void AudioBackendNone::Source_PanChanged(uint32 sourceID, float pan)
{
}
void AudioBackendNone::Source_SpatialSetupChanged(AudioSource* source)
void AudioBackendNone::Source_IsLoopingChanged(uint32 sourceID, bool loop)
{
}
void AudioBackendNone::Source_ClipLoaded(AudioSource* source)
void AudioBackendNone::Source_SpatialSetupChanged(uint32 sourceID, bool spatial, float attenuation, float minDistance, float doppler)
{
}
void AudioBackendNone::Source_Cleanup(AudioSource* source)
void AudioBackendNone::Source_Play(uint32 sourceID)
{
}
void AudioBackendNone::Source_Play(AudioSource* source)
void AudioBackendNone::Source_Pause(uint32 sourceID)
{
}
void AudioBackendNone::Source_Pause(AudioSource* source)
void AudioBackendNone::Source_Stop(uint32 sourceID)
{
}
void AudioBackendNone::Source_Stop(AudioSource* source)
void AudioBackendNone::Source_SetCurrentBufferTime(uint32 sourceID, float value)
{
}
void AudioBackendNone::Source_SetCurrentBufferTime(AudioSource* source, float value)
{
}
float AudioBackendNone::Source_GetCurrentBufferTime(const AudioSource* source)
float AudioBackendNone::Source_GetCurrentBufferTime(uint32 sourceID)
{
return 0.0f;
}
void AudioBackendNone::Source_SetNonStreamingBuffer(AudioSource* source)
void AudioBackendNone::Source_SetNonStreamingBuffer(uint32 sourceID, uint32 bufferID)
{
}
void AudioBackendNone::Source_GetProcessedBuffersCount(AudioSource* source, int32& processedBuffersCount)
void AudioBackendNone::Source_GetProcessedBuffersCount(uint32 sourceID, int32& processedBuffersCount)
{
processedBuffersCount = 0;
}
void AudioBackendNone::Source_GetQueuedBuffersCount(AudioSource* source, int32& queuedBuffersCount)
void AudioBackendNone::Source_GetQueuedBuffersCount(uint32 sourceID, int32& queuedBuffersCount)
{
}
void AudioBackendNone::Source_QueueBuffer(AudioSource* source, uint32 bufferId)
void AudioBackendNone::Source_QueueBuffer(uint32 sourceID, uint32 bufferID)
{
}
void AudioBackendNone::Source_DequeueProcessedBuffers(AudioSource* source)
void AudioBackendNone::Source_DequeueProcessedBuffers(uint32 sourceID)
{
}
@@ -115,11 +106,11 @@ uint32 AudioBackendNone::Buffer_Create()
return 1;
}
void AudioBackendNone::Buffer_Delete(uint32 bufferId)
void AudioBackendNone::Buffer_Delete(uint32 bufferID)
{
}
void AudioBackendNone::Buffer_Write(uint32 bufferId, byte* samples, const AudioDataInfo& info)
void AudioBackendNone::Buffer_Write(uint32 bufferID, byte* samples, const AudioDataInfo& info)
{
}

View File

@@ -17,30 +17,28 @@ public:
void Listener_VelocityChanged(const Vector3& velocity) override;
void Listener_TransformChanged(const Vector3& position, const Quaternion& orientation) override;
void Listener_ReinitializeAll() override;
void Source_OnAdd(AudioSource* source) override;
void Source_OnRemove(AudioSource* source) override;
void Source_VelocityChanged(AudioSource* source) override;
void Source_TransformChanged(AudioSource* source) override;
void Source_VolumeChanged(AudioSource* source) override;
void Source_PitchChanged(AudioSource* source) override;
void Source_PanChanged(AudioSource* source) override;
void Source_IsLoopingChanged(AudioSource* source) override;
void Source_SpatialSetupChanged(AudioSource* source) override;
void Source_ClipLoaded(AudioSource* source) override;
void Source_Cleanup(AudioSource* source) override;
void Source_Play(AudioSource* source) override;
void Source_Pause(AudioSource* source) override;
void Source_Stop(AudioSource* source) override;
void Source_SetCurrentBufferTime(AudioSource* source, float value) override;
float Source_GetCurrentBufferTime(const AudioSource* source) override;
void Source_SetNonStreamingBuffer(AudioSource* source) override;
void Source_GetProcessedBuffersCount(AudioSource* source, int32& processedBuffersCount) override;
void Source_GetQueuedBuffersCount(AudioSource* source, int32& queuedBuffersCount) override;
void Source_QueueBuffer(AudioSource* source, uint32 bufferId) override;
void Source_DequeueProcessedBuffers(AudioSource* source) override;
uint32 Source_Add(const AudioDataInfo& format, const Vector3& position, const Quaternion& orientation, float volume, float pitch, float pan, bool loop, bool spatial, float attenuation, float minDistance, float doppler) override;
void Source_Remove(uint32 sourceID) override;
void Source_VelocityChanged(uint32 sourceID, const Vector3& velocity) override;
void Source_TransformChanged(uint32 sourceID, const Vector3& position, const Quaternion& orientation) override;
void Source_VolumeChanged(uint32 sourceID, float volume) override;
void Source_PitchChanged(uint32 sourceID, float pitch) override;
void Source_PanChanged(uint32 sourceID, float pan) override;
void Source_IsLoopingChanged(uint32 sourceID, bool loop) override;
void Source_SpatialSetupChanged(uint32 sourceID, bool spatial, float attenuation, float minDistance, float doppler) override;
void Source_Play(uint32 sourceID) override;
void Source_Pause(uint32 sourceID) override;
void Source_Stop(uint32 sourceID) override;
void Source_SetCurrentBufferTime(uint32 sourceID, float value) override;
float Source_GetCurrentBufferTime(uint32 sourceID) override;
void Source_SetNonStreamingBuffer(uint32 sourceID, uint32 bufferID) override;
void Source_GetProcessedBuffersCount(uint32 sourceID, int32& processedBuffersCount) override;
void Source_GetQueuedBuffersCount(uint32 sourceID, int32& queuedBuffersCount) override;
void Source_QueueBuffer(uint32 sourceID, uint32 bufferID) override;
void Source_DequeueProcessedBuffers(uint32 sourceID) override;
uint32 Buffer_Create() override;
void Buffer_Delete(uint32 bufferId) override;
void Buffer_Write(uint32 bufferId, byte* samples, const AudioDataInfo& info) override;
void Buffer_Delete(uint32 bufferID) override;
void Buffer_Write(uint32 bufferID, byte* samples, const AudioDataInfo& info) override;
const Char* Base_Name() override;
FeatureFlags Base_Features() override;
void Base_OnActiveDeviceChanged() override;

View File

@@ -5,6 +5,7 @@
#include "AudioBackendOAL.h"
#include "Engine/Platform/StringUtils.h"
#include "Engine/Core/Log.h"
#include "Engine/Core/Collections/Dictionary.h"
#include "Engine/Tools/AudioTool/AudioTool.h"
#include "Engine/Engine/Units.h"
#include "Engine/Profiler/ProfilerCPU.h"
@@ -42,6 +43,8 @@ namespace ALC
ALCdevice* Device = nullptr;
ALCcontext* Context = nullptr;
AudioBackend::FeatureFlags Features = AudioBackend::FeatureFlags::None;
CriticalSection Locker;
Dictionary<uint32, AudioDataInfo> SourceIDtoFormat;
bool IsExtensionSupported(const char* extension)
{
@@ -75,32 +78,28 @@ namespace ALC
namespace Source
{
void Rebuild(AudioSource* source)
void Rebuild(uint32& sourceID, const Vector3& position, const Quaternion& orientation, float volume, float pitch, float pan, bool loop, bool spatial, float attenuation, float minDistance, float doppler)
{
ASSERT(source->SourceID == 0);
const bool is3D = source->Is3D();
const bool loop = source->GetIsLooping() && !source->UseStreaming();
uint32 sourceID = 0;
ASSERT_LOW_LAYER(sourceID == 0);
alGenSources(1, &sourceID);
source->SourceID = sourceID;
ASSERT_LOW_LAYER(sourceID != 0);
alSourcef(sourceID, AL_GAIN, source->GetVolume());
alSourcef(sourceID, AL_PITCH, source->GetPitch());
alSourcef(sourceID, AL_GAIN, volume);
alSourcef(sourceID, AL_PITCH, pitch);
alSourcef(sourceID, AL_SEC_OFFSET, 0.0f);
alSourcei(sourceID, AL_LOOPING, loop);
alSourcei(sourceID, AL_SOURCE_RELATIVE, !is3D);
alSourcei(sourceID, AL_SOURCE_RELATIVE, !spatial);
alSourcei(sourceID, AL_BUFFER, 0);
if (is3D)
if (spatial)
{
#ifdef AL_SOFT_source_spatialize
alSourcei(sourceID, AL_SOURCE_SPATIALIZE_SOFT, AL_TRUE);
#endif
alSourcef(sourceID, AL_ROLLOFF_FACTOR, source->GetAttenuation());
alSourcef(sourceID, AL_DOPPLER_FACTOR, source->GetDopplerFactor());
alSourcef(sourceID, AL_REFERENCE_DISTANCE, FLAX_DST_TO_OAL(source->GetMinDistance()));
alSource3f(sourceID, AL_POSITION, FLAX_POS_TO_OAL(source->GetPosition()));
alSource3f(sourceID, AL_VELOCITY, FLAX_VEL_TO_OAL(source->GetVelocity()));
alSourcef(sourceID, AL_ROLLOFF_FACTOR, attenuation);
alSourcef(sourceID, AL_DOPPLER_FACTOR, doppler);
alSourcef(sourceID, AL_REFERENCE_DISTANCE, FLAX_DST_TO_OAL(minDistance));
alSource3f(sourceID, AL_POSITION, FLAX_POS_TO_OAL(position));
alSource3f(sourceID, AL_VELOCITY, FLAX_VEL_TO_OAL(Vector3::Zero));
}
else
{
@@ -111,26 +110,23 @@ namespace ALC
alSource3f(sourceID, AL_VELOCITY, 0.0f, 0.0f, 0.0f);
}
#ifdef AL_EXT_STEREO_ANGLES
const float panAngle = source->GetPan() * PI_HALF;
const float panAngle = pan * PI_HALF;
const ALfloat panAngles[2] = { (ALfloat)(PI / 6.0 - panAngle), (ALfloat)(-PI / 6.0 - panAngle) }; // Angles are specified counter-clockwise in radians
alSourcefv(sourceID, AL_STEREO_ANGLES, panAngles);
#endif
// Restore state after Cleanup
source->Restore();
}
}
void RebuildContext(bool isChangingDevice)
struct AudioSourceState
{
AudioSource::States State;
float Time;
};
void RebuildContext(const Array<AudioSourceState>& states)
{
LOG(Info, "Rebuilding audio contexts");
if (!isChangingDevice)
{
for (AudioSource* source : Audio::Sources)
source->Cleanup();
}
ClearContext();
if (Device == nullptr)
@@ -150,8 +146,39 @@ namespace ALC
for (AudioListener* listener : Audio::Listeners)
Listener::Rebuild(listener);
for (AudioSource* source : Audio::Sources)
Source::Rebuild(source);
for (int32 i = 0; i < states.Count(); i++)
{
AudioSource* source = Audio::Sources[i];
Source::Rebuild(source->SourceID, source->GetPosition(), source->GetOrientation(), source->GetVolume(), source->GetPitch(), source->GetPan(), source->GetIsLooping() && !source->UseStreaming(), source->Is3D(), source->GetAttenuation(), source->GetMinDistance(), source->GetDopplerFactor());
if (states.HasItems())
{
// Restore playback state
auto& state = states[i];
if (state.State != AudioSource::States::Stopped)
source->Play();
if (state.State == AudioSource::States::Paused)
source->Pause();
if (state.State != AudioSource::States::Stopped)
source->SetTime(state.Time);
}
}
}
void RebuildContext(bool isChangingDevice)
{
Array<AudioSourceState> states;
if (!isChangingDevice)
{
states.EnsureCapacity(Audio::Sources.Count());
for (AudioSource* source : Audio::Sources)
{
states.Add({ source->GetState(), source->GetTime() });
source->Stop();
}
}
RebuildContext(states);
}
}
@@ -269,74 +296,76 @@ void AudioBackendOAL::Listener_ReinitializeAll()
ALC::RebuildContext(false);
}
void AudioBackendOAL::Source_OnAdd(AudioSource* source)
uint32 AudioBackendOAL::Source_Add(const AudioDataInfo& format, const Vector3& position, const Quaternion& orientation, float volume, float pitch, float pan, bool loop, bool spatial, float attenuation, float minDistance, float doppler)
{
ALC::Source::Rebuild(source);
uint32 sourceID = 0;
ALC::Source::Rebuild(sourceID, position, orientation, volume, pitch, pan, loop, spatial, attenuation, minDistance, doppler);
// Cache audio data format assigned on source (used in Source_GetCurrentBufferTime)
ALC::Locker.Lock();
ALC::SourceIDtoFormat[sourceID] = format;
ALC::Locker.Unlock();
return sourceID;
}
void AudioBackendOAL::Source_OnRemove(AudioSource* source)
void AudioBackendOAL::Source_Remove(uint32 sourceID)
{
source->Cleanup();
alSourcei(sourceID, AL_BUFFER, 0);
ALC_CHECK_ERROR(alSourcei);
alDeleteSources(1, &sourceID);
ALC_CHECK_ERROR(alDeleteSources);
ALC::Locker.Lock();
ALC::SourceIDtoFormat.Remove(sourceID);
ALC::Locker.Unlock();
}
void AudioBackendOAL::Source_VelocityChanged(AudioSource* source)
void AudioBackendOAL::Source_VelocityChanged(uint32 sourceID, const Vector3& velocity)
{
if (!source->Is3D())
return;
const uint32 sourceID = source->SourceID;
alSource3f(sourceID, AL_VELOCITY, FLAX_VEL_TO_OAL(source->GetVelocity()));
alSource3f(sourceID, AL_VELOCITY, FLAX_VEL_TO_OAL(velocity));
}
void AudioBackendOAL::Source_TransformChanged(AudioSource* source)
void AudioBackendOAL::Source_TransformChanged(uint32 sourceID, const Vector3& position, const Quaternion& orientation)
{
if (!source->Is3D())
return;
const uint32 sourceID = source->SourceID;
alSource3f(sourceID, AL_POSITION, FLAX_POS_TO_OAL(source->GetPosition()));
alSource3f(sourceID, AL_POSITION, FLAX_POS_TO_OAL(position));
}
void AudioBackendOAL::Source_VolumeChanged(AudioSource* source)
void AudioBackendOAL::Source_VolumeChanged(uint32 sourceID, float volume)
{
const uint32 sourceID = source->SourceID;
alSourcef(sourceID, AL_GAIN, source->GetVolume());
alSourcef(sourceID, AL_GAIN, volume);
}
void AudioBackendOAL::Source_PitchChanged(AudioSource* source)
void AudioBackendOAL::Source_PitchChanged(uint32 sourceID, float pitch)
{
const uint32 sourceID = source->SourceID;
alSourcef(sourceID, AL_PITCH, source->GetPitch());
alSourcef(sourceID, AL_PITCH, pitch);
}
void AudioBackendOAL::Source_PanChanged(AudioSource* source)
void AudioBackendOAL::Source_PanChanged(uint32 sourceID, float pan)
{
#ifdef AL_EXT_STEREO_ANGLES
const float panAngle = source->GetPan() * PI_HALF;
const float panAngle = pan * PI_HALF;
const ALfloat panAngles[2] = { (ALfloat)(PI / 6.0 - panAngle), (ALfloat)(-PI / 6.0 - panAngle) }; // Angles are specified counter-clockwise in radians
const uint32 sourceID = source->SourceID;
alSourcefv(sourceID, AL_STEREO_ANGLES, panAngles);
#endif
}
void AudioBackendOAL::Source_IsLoopingChanged(AudioSource* source)
void AudioBackendOAL::Source_IsLoopingChanged(uint32 sourceID, bool loop)
{
const bool loop = source->GetIsLooping() && !source->UseStreaming();
const uint32 sourceID = source->SourceID;
alSourcei(sourceID, AL_LOOPING, loop);
}
void AudioBackendOAL::Source_SpatialSetupChanged(AudioSource* source)
void AudioBackendOAL::Source_SpatialSetupChanged(uint32 sourceID, bool spatial, float attenuation, float minDistance, float doppler)
{
const bool is3D = source->Is3D();
const uint32 sourceID = source->SourceID;
alSourcei(sourceID, AL_SOURCE_RELATIVE, !is3D);
if (is3D)
alSourcei(sourceID, AL_SOURCE_RELATIVE, !spatial);
if (spatial)
{
#ifdef AL_SOFT_source_spatialize
alSourcei(sourceID, AL_SOURCE_SPATIALIZE_SOFT, AL_TRUE);
#endif
alSourcef(sourceID, AL_ROLLOFF_FACTOR, source->GetAttenuation());
alSourcef(sourceID, AL_DOPPLER_FACTOR, source->GetDopplerFactor());
alSourcef(sourceID, AL_REFERENCE_DISTANCE, FLAX_DST_TO_OAL(source->GetMinDistance()));
alSourcef(sourceID, AL_ROLLOFF_FACTOR, attenuation);
alSourcef(sourceID, AL_DOPPLER_FACTOR, doppler);
alSourcef(sourceID, AL_REFERENCE_DISTANCE, FLAX_DST_TO_OAL(minDistance));
}
else
{
@@ -346,46 +375,20 @@ void AudioBackendOAL::Source_SpatialSetupChanged(AudioSource* source)
}
}
void AudioBackendOAL::Source_ClipLoaded(AudioSource* source)
void AudioBackendOAL::Source_Play(uint32 sourceID)
{
if (source->SourceID == 0)
return;
const auto clip = source->Clip.Get();
const bool is3D = source->Is3D();
const bool loop = source->GetIsLooping() && !clip->IsStreamable();
const uint32 sourceID = source->SourceID;
alSourcei(sourceID, AL_SOURCE_RELATIVE, !is3D);
alSourcei(sourceID, AL_LOOPING, loop);
}
void AudioBackendOAL::Source_Cleanup(AudioSource* source)
{
const uint32 sourceID = source->SourceID;
alSourcei(sourceID, AL_BUFFER, 0);
ALC_CHECK_ERROR(alSourcei);
alDeleteSources(1, &sourceID);
ALC_CHECK_ERROR(alDeleteSources);
}
void AudioBackendOAL::Source_Play(AudioSource* source)
{
const uint32 sourceID = source->SourceID;
alSourcePlay(sourceID);
ALC_CHECK_ERROR(alSourcePlay);
}
void AudioBackendOAL::Source_Pause(AudioSource* source)
void AudioBackendOAL::Source_Pause(uint32 sourceID)
{
const uint32 sourceID = source->SourceID;
alSourcePause(sourceID);
ALC_CHECK_ERROR(alSourcePause);
}
void AudioBackendOAL::Source_Stop(AudioSource* source)
void AudioBackendOAL::Source_Stop(uint32 sourceID)
{
const uint32 sourceID = source->SourceID;
// Stop and rewind
alSourceRewind(sourceID);
ALC_CHECK_ERROR(alSourceRewind);
@@ -396,67 +399,61 @@ void AudioBackendOAL::Source_Stop(AudioSource* source)
ALC_CHECK_ERROR(alSourcei);
}
void AudioBackendOAL::Source_SetCurrentBufferTime(AudioSource* source, float value)
void AudioBackendOAL::Source_SetCurrentBufferTime(uint32 sourceID, float value)
{
const uint32 sourceID = source->SourceID;
alSourcef(sourceID, AL_SEC_OFFSET, value);
}
float AudioBackendOAL::Source_GetCurrentBufferTime(const AudioSource* source)
float AudioBackendOAL::Source_GetCurrentBufferTime(uint32 sourceID)
{
#if 0
float time;
alGetSourcef(source->SourceID, AL_SEC_OFFSET, &time);
alGetSourcef(sourceID, AL_SEC_OFFSET, &time);
#else
ASSERT(source->Clip && source->Clip->IsLoaded());
const AudioDataInfo& clipInfo = source->Clip->AudioHeader.Info;
ALC::Locker.Lock();
AudioDataInfo clipInfo = ALC::SourceIDtoFormat[sourceID];
ALC::Locker.Unlock();
ALint samplesPlayed;
alGetSourcei(source->SourceID, AL_SAMPLE_OFFSET, &samplesPlayed);
alGetSourcei(sourceID, AL_SAMPLE_OFFSET, &samplesPlayed);
const uint32 totalSamples = clipInfo.NumSamples / clipInfo.NumChannels;
const float time = (samplesPlayed % totalSamples) / static_cast<float>(Math::Max(1U, clipInfo.SampleRate));
if (totalSamples > 0)
samplesPlayed %= totalSamples;
const float time = samplesPlayed / static_cast<float>(Math::Max(1U, clipInfo.SampleRate));
#endif
return time;
}
void AudioBackendOAL::Source_SetNonStreamingBuffer(AudioSource* source)
void AudioBackendOAL::Source_SetNonStreamingBuffer(uint32 sourceID, uint32 bufferID)
{
const uint32 bufferId = source->Clip->Buffers[0];
const uint32 sourceID = source->SourceID;
alSourcei(sourceID, AL_BUFFER, bufferId);
alSourcei(sourceID, AL_BUFFER, bufferID);
ALC_CHECK_ERROR(alSourcei);
}
void AudioBackendOAL::Source_GetProcessedBuffersCount(AudioSource* source, int32& processedBuffersCount)
void AudioBackendOAL::Source_GetProcessedBuffersCount(uint32 sourceID, int32& processedBuffersCount)
{
// Check the first context only
const uint32 sourceID = source->SourceID;
alGetSourcei(sourceID, AL_BUFFERS_PROCESSED, &processedBuffersCount);
ALC_CHECK_ERROR(alGetSourcei);
}
void AudioBackendOAL::Source_GetQueuedBuffersCount(AudioSource* source, int32& queuedBuffersCount)
void AudioBackendOAL::Source_GetQueuedBuffersCount(uint32 sourceID, int32& queuedBuffersCount)
{
// Check the first context only
const uint32 sourceID = source->SourceID;
alGetSourcei(sourceID, AL_BUFFERS_QUEUED, &queuedBuffersCount);
ALC_CHECK_ERROR(alGetSourcei);
}
void AudioBackendOAL::Source_QueueBuffer(AudioSource* source, uint32 bufferId)
void AudioBackendOAL::Source_QueueBuffer(uint32 sourceID, uint32 bufferID)
{
const uint32 sourceID = source->SourceID;
// Queue new buffer
alSourceQueueBuffers(sourceID, 1, &bufferId);
alSourceQueueBuffers(sourceID, 1, &bufferID);
ALC_CHECK_ERROR(alSourceQueueBuffers);
}
void AudioBackendOAL::Source_DequeueProcessedBuffers(AudioSource* source)
void AudioBackendOAL::Source_DequeueProcessedBuffers(uint32 sourceID)
{
ALuint buffers[AUDIO_MAX_SOURCE_BUFFERS];
const uint32 sourceID = source->SourceID;
int32 numProcessedBuffers;
ALuint buffers[AUDIO_MAX_SOURCE_BUFFERS];
alGetSourcei(sourceID, AL_BUFFERS_PROCESSED, &numProcessedBuffers);
alSourceUnqueueBuffers(sourceID, numProcessedBuffers, buffers);
ALC_CHECK_ERROR(alSourceUnqueueBuffers);
@@ -464,19 +461,19 @@ void AudioBackendOAL::Source_DequeueProcessedBuffers(AudioSource* source)
uint32 AudioBackendOAL::Buffer_Create()
{
uint32 bufferId;
alGenBuffers(1, &bufferId);
uint32 bufferID;
alGenBuffers(1, &bufferID);
ALC_CHECK_ERROR(alGenBuffers);
return bufferId;
return bufferID;
}
void AudioBackendOAL::Buffer_Delete(uint32 bufferId)
void AudioBackendOAL::Buffer_Delete(uint32 bufferID)
{
alDeleteBuffers(1, &bufferId);
alDeleteBuffers(1, &bufferID);
ALC_CHECK_ERROR(alDeleteBuffers);
}
void AudioBackendOAL::Buffer_Write(uint32 bufferId, byte* samples, const AudioDataInfo& info)
void AudioBackendOAL::Buffer_Write(uint32 bufferID, byte* samples, const AudioDataInfo& info)
{
PROFILE_CPU();
@@ -495,7 +492,7 @@ void AudioBackendOAL::Buffer_Write(uint32 bufferId, byte* samples, const AudioDa
AudioTool::ConvertToFloat(samples, info.BitDepth, sampleBufferFloat, info.NumSamples);
format = GetOpenALBufferFormat(info.NumChannels, 32);
alBufferData(bufferId, format, sampleBufferFloat, bufferSize, info.SampleRate);
alBufferData(bufferID, format, sampleBufferFloat, bufferSize, info.SampleRate);
ALC_CHECK_ERROR(alBufferData);
Allocator::Free(sampleBufferFloat);
}
@@ -507,7 +504,7 @@ void AudioBackendOAL::Buffer_Write(uint32 bufferId, byte* samples, const AudioDa
AudioTool::ConvertBitDepth(samples, info.BitDepth, sampleBuffer16, 16, info.NumSamples);
format = GetOpenALBufferFormat(info.NumChannels, 16);
alBufferData(bufferId, format, sampleBuffer16, bufferSize, info.SampleRate);
alBufferData(bufferID, format, sampleBuffer16, bufferSize, info.SampleRate);
ALC_CHECK_ERROR(alBufferData);
Allocator::Free(sampleBuffer16);
}
@@ -520,13 +517,13 @@ void AudioBackendOAL::Buffer_Write(uint32 bufferId, byte* samples, const AudioDa
for (uint32 i = 0; i < info.NumSamples; i++)
sampleBuffer[i] = ((int8*)samples)[i] + 128;
alBufferData(bufferId, format, sampleBuffer, bufferSize, info.SampleRate);
alBufferData(bufferID, format, sampleBuffer, bufferSize, info.SampleRate);
ALC_CHECK_ERROR(alBufferData);
Allocator::Free(sampleBuffer);
}
else if (format)
{
alBufferData(bufferId, format, samples, info.NumSamples * (info.BitDepth / 8), info.SampleRate);
alBufferData(bufferID, format, samples, info.NumSamples * (info.BitDepth / 8), info.SampleRate);
ALC_CHECK_ERROR(alBufferData);
}
}
@@ -543,7 +540,7 @@ void AudioBackendOAL::Buffer_Write(uint32 bufferId, byte* samples, const AudioDa
AudioTool::ConvertBitDepth(samples, info.BitDepth, sampleBuffer32, 32, info.NumSamples);
format = GetOpenALBufferFormat(info.NumChannels, 32);
alBufferData(bufferId, format, sampleBuffer32, bufferSize, info.SampleRate);
alBufferData(bufferID, format, sampleBuffer32, bufferSize, info.SampleRate);
ALC_CHECK_ERROR(alBufferData);
Allocator::Free(sampleBuffer32);
@@ -558,14 +555,14 @@ void AudioBackendOAL::Buffer_Write(uint32 bufferId, byte* samples, const AudioDa
sampleBuffer[i] = ((int8*)samples)[i] + 128;
format = GetOpenALBufferFormat(info.NumChannels, 16);
alBufferData(bufferId, format, sampleBuffer, bufferSize, info.SampleRate);
alBufferData(bufferID, format, sampleBuffer, bufferSize, info.SampleRate);
ALC_CHECK_ERROR(alBufferData);
Allocator::Free(sampleBuffer);
}
else if (format)
{
alBufferData(bufferId, format, samples, info.NumSamples * (info.BitDepth / 8), info.SampleRate);
alBufferData(bufferID, format, samples, info.NumSamples * (info.BitDepth / 8), info.SampleRate);
ALC_CHECK_ERROR(alBufferData);
}
}
@@ -589,8 +586,18 @@ AudioBackend::FeatureFlags AudioBackendOAL::Base_Features()
void AudioBackendOAL::Base_OnActiveDeviceChanged()
{
// Cleanup
Array<ALC::AudioSourceState> states;
states.EnsureCapacity(Audio::Sources.Count());
for (AudioSource* source : Audio::Sources)
source->Cleanup();
{
states.Add({ source->GetState(), source->GetTime() });
source->Stop();
if (source->SourceID)
{
Source_Remove(source->SourceID);
source->SourceID = 0;
}
}
ALC::ClearContext();
if (ALC::Device != nullptr)
{
@@ -608,7 +615,7 @@ void AudioBackendOAL::Base_OnActiveDeviceChanged()
}
// Setup
ALC::RebuildContext(true);
ALC::RebuildContext(states);
}
void AudioBackendOAL::Base_SetDopplerFactor(float value)

View File

@@ -17,30 +17,28 @@ public:
void Listener_VelocityChanged(const Vector3& velocity) override;
void Listener_TransformChanged(const Vector3& position, const Quaternion& orientation) override;
void Listener_ReinitializeAll() override;
void Source_OnAdd(AudioSource* source) override;
void Source_OnRemove(AudioSource* source) override;
void Source_VelocityChanged(AudioSource* source) override;
void Source_TransformChanged(AudioSource* source) override;
void Source_VolumeChanged(AudioSource* source) override;
void Source_PitchChanged(AudioSource* source) override;
void Source_PanChanged(AudioSource* source) override;
void Source_IsLoopingChanged(AudioSource* source) override;
void Source_SpatialSetupChanged(AudioSource* source) override;
void Source_ClipLoaded(AudioSource* source) override;
void Source_Cleanup(AudioSource* source) override;
void Source_Play(AudioSource* source) override;
void Source_Pause(AudioSource* source) override;
void Source_Stop(AudioSource* source) override;
void Source_SetCurrentBufferTime(AudioSource* source, float value) override;
float Source_GetCurrentBufferTime(const AudioSource* source) override;
void Source_SetNonStreamingBuffer(AudioSource* source) override;
void Source_GetProcessedBuffersCount(AudioSource* source, int32& processedBuffersCount) override;
void Source_GetQueuedBuffersCount(AudioSource* source, int32& queuedBuffersCount) override;
void Source_QueueBuffer(AudioSource* source, uint32 bufferId) override;
void Source_DequeueProcessedBuffers(AudioSource* source) override;
uint32 Source_Add(const AudioDataInfo& format, const Vector3& position, const Quaternion& orientation, float volume, float pitch, float pan, bool loop, bool spatial, float attenuation, float minDistance, float doppler) override;
void Source_Remove(uint32 sourceID) override;
void Source_VelocityChanged(uint32 sourceID, const Vector3& velocity) override;
void Source_TransformChanged(uint32 sourceID, const Vector3& position, const Quaternion& orientation) override;
void Source_VolumeChanged(uint32 sourceID, float volume) override;
void Source_PitchChanged(uint32 sourceID, float pitch) override;
void Source_PanChanged(uint32 sourceID, float pan) override;
void Source_IsLoopingChanged(uint32 sourceID, bool loop) override;
void Source_SpatialSetupChanged(uint32 sourceID, bool spatial, float attenuation, float minDistance, float doppler) override;
void Source_Play(uint32 sourceID) override;
void Source_Pause(uint32 sourceID) override;
void Source_Stop(uint32 sourceID) override;
void Source_SetCurrentBufferTime(uint32 sourceID, float value) override;
float Source_GetCurrentBufferTime(uint32 sourceID) override;
void Source_SetNonStreamingBuffer(uint32 sourceID, uint32 bufferID) override;
void Source_GetProcessedBuffersCount(uint32 sourceID, int32& processedBuffersCount) override;
void Source_GetQueuedBuffersCount(uint32 sourceID, int32& queuedBuffersCount) override;
void Source_QueueBuffer(uint32 sourceID, uint32 bufferID) override;
void Source_DequeueProcessedBuffers(uint32 sourceID) override;
uint32 Buffer_Create() override;
void Buffer_Delete(uint32 bufferId) override;
void Buffer_Write(uint32 bufferId, byte* samples, const AudioDataInfo& info) override;
void Buffer_Delete(uint32 bufferID) override;
void Buffer_Write(uint32 bufferID, byte* samples, const AudioDataInfo& info) override;
const Char* Base_Name() override;
FeatureFlags Base_Features() override;
void Base_OnActiveDeviceChanged() override;

View File

@@ -8,7 +8,6 @@
#include "Engine/Core/Collections/ChunkedArray.h"
#include "Engine/Core/Log.h"
#include "Engine/Audio/Audio.h"
#include "Engine/Audio/AudioSource.h"
#include "Engine/Threading/Threading.h"
#if PLATFORM_WINDOWS
@@ -76,7 +75,7 @@ namespace XAudio2
}
public:
AudioSource* Source;
uint32 SourceID;
void PeekSamples();
};
@@ -85,6 +84,7 @@ namespace XAudio2
{
IXAudio2SourceVoice* Voice;
WAVEFORMATEX Format;
AudioDataInfo Info;
XAUDIO2_SEND_DESCRIPTOR Destination;
float StartTimeForQueueBuffer;
float LastBufferStartTime;
@@ -93,6 +93,8 @@ namespace XAudio2
int32 Channels;
bool IsDirty;
bool IsPlaying;
bool IsLoop;
uint32 LastBufferID;
VoiceCallback Callback;
Source()
@@ -112,6 +114,8 @@ namespace XAudio2
IsDirty = false;
Is3D = false;
IsPlaying = false;
IsLoop = false;
LastBufferID = 0;
LastBufferStartSamplesPlayed = 0;
BuffersProcessed = 0;
}
@@ -120,17 +124,6 @@ namespace XAudio2
{
return Voice == nullptr;
}
void UpdateTransform(const AudioSource* source)
{
Position = source->GetPosition();
Orientation = source->GetOrientation();
}
void UpdateVelocity(const AudioSource* source)
{
Velocity = source->GetVelocity();
}
};
struct Buffer
@@ -166,11 +159,11 @@ namespace XAudio2
ChunkedArray<Buffer*, 64> Buffers; // TODO: use ChunkedArray for better performance or use buffers pool?
EngineCallback Callback;
Source* GetSource(const AudioSource* source)
Source* GetSource(uint32 sourceID)
{
if (source->SourceID == 0)
if (sourceID == 0)
return nullptr;
return &Sources[source->SourceID - 1]; // 0 is invalid ID so shift them
return &Sources[sourceID - 1]; // 0 is invalid ID so shift them
}
void MarkAllDirty()
@@ -178,9 +171,9 @@ namespace XAudio2
ForceDirty = true;
}
void QueueBuffer(Source* aSource, const AudioSource* source, const int32 bufferId, XAUDIO2_BUFFER& buffer)
void QueueBuffer(Source* aSource, const int32 bufferID, XAUDIO2_BUFFER& buffer)
{
Buffer* aBuffer = Buffers[bufferId - 1];
Buffer* aBuffer = Buffers[bufferID - 1];
buffer.pAudioData = aBuffer->Data.Get();
buffer.AudioBytes = aBuffer->Data.Count();
@@ -200,14 +193,14 @@ namespace XAudio2
void VoiceCallback::OnBufferEnd(void* pBufferContext)
{
auto aSource = GetSource(Source);
auto aSource = GetSource(SourceID);
if (aSource->IsPlaying)
aSource->BuffersProcessed++;
}
void VoiceCallback::PeekSamples()
{
auto aSource = GetSource(Source);
auto aSource = GetSource(SourceID);
XAUDIO2_VOICE_STATE state;
aSource->Voice->GetState(&state);
aSource->LastBufferStartSamplesPlayed = state.SamplesPlayed;
@@ -216,7 +209,7 @@ namespace XAudio2
void AudioBackendXAudio2::Listener_Reset()
{
XAudio2::Listener->Reset();
XAudio2::Listener.Reset();
XAudio2::MarkAllDirty();
}
@@ -238,17 +231,13 @@ void AudioBackendXAudio2::Listener_ReinitializeAll()
// TODO: Implement XAudio2 reinitialization; read HRTF audio value from Audio class
}
void AudioBackendXAudio2::Source_OnAdd(AudioSource* source)
uint32 AudioBackendXAudio2::Source_Add(const AudioDataInfo& format, const Vector3& position, const Quaternion& orientation, float volume, float pitch, float pan, bool loop, bool spatial, float attenuation, float minDistance, float doppler)
{
// Skip if has no clip (needs audio data to create a source - needs data format information)
if (source->Clip == nullptr || !source->Clip->IsLoaded())
return;
auto clip = source->Clip.Get();
ScopeLock lock(XAudio2::Locker);
// Get first free source
XAudio2::Source* aSource = nullptr;
uint32 sourceID;
uint32 sourceID = 0;
for (int32 i = 0; i < XAudio2::Sources.Count(); i++)
{
if (XAudio2::Sources[i].IsFree())
@@ -266,115 +255,124 @@ void AudioBackendXAudio2::Source_OnAdd(AudioSource* source)
XAudio2::Sources.Add(src);
aSource = &XAudio2::Sources[sourceID];
}
sourceID++; // 0 is invalid ID so shift them
// Initialize audio data format information (from clip)
const auto& header = clip->AudioHeader;
auto& format = aSource->Format;
format.wFormatTag = WAVE_FORMAT_PCM;
format.nChannels = clip->Is3D() ? 1 : header.Info.NumChannels; // 3d audio is always mono (AudioClip auto-converts before buffer write if FeatureFlags::SpatialMultiChannel is unset)
format.nSamplesPerSec = header.Info.SampleRate;
format.wBitsPerSample = header.Info.BitDepth;
format.nBlockAlign = (WORD)(format.nChannels * (format.wBitsPerSample / 8));
format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
format.cbSize = 0;
aSource->Info = format;
auto& aFormat = aSource->Format;
aFormat.wFormatTag = WAVE_FORMAT_PCM;
aFormat.nChannels = spatial ? 1 : format.NumChannels; // 3d audio is always mono (AudioClip auto-converts before buffer write if FeatureFlags::SpatialMultiChannel is unset)
aFormat.nSamplesPerSec = format.SampleRate;
aFormat.wBitsPerSample = format.BitDepth;
aFormat.nBlockAlign = (WORD)(aFormat.nChannels * (aFormat.wBitsPerSample / 8));
aFormat.nAvgBytesPerSec = aFormat.nSamplesPerSec * aFormat.nBlockAlign;
aFormat.cbSize = 0;
// Setup dry effect
aSource->Destination.pOutputVoice = XAudio2::MasteringVoice;
// Create voice
const XAUDIO2_VOICE_SENDS sendList =
{
1,
&aSource->Destination
};
const XAUDIO2_VOICE_SENDS sendList = { 1, &aSource->Destination };
HRESULT hr = XAudio2::Instance->CreateSourceVoice(&aSource->Voice, &aSource->Format, 0, 2.0f, &aSource->Callback, &sendList);
XAUDIO2_CHECK_ERROR(CreateSourceVoice);
if (FAILED(hr))
return;
source->SourceID = sourceID + 1; // 0 is invalid ID so shift them
return 0;
// Prepare source state
aSource->Callback.Source = source;
aSource->Callback.SourceID = sourceID;
aSource->IsDirty = true;
aSource->Is3D = source->Is3D();
aSource->Pitch = source->GetPitch();
aSource->Pan = source->GetPan();
aSource->DopplerFactor = source->GetDopplerFactor();
aSource->Volume = source->GetVolume();
aSource->MinDistance = source->GetMinDistance();
aSource->Attenuation = source->GetAttenuation();
aSource->Channels = format.nChannels;
aSource->UpdateTransform(source);
aSource->UpdateVelocity(source);
hr = aSource->Voice->SetVolume(source->GetVolume());
aSource->IsLoop = loop;
aSource->Is3D = spatial;
aSource->Pitch = pitch;
aSource->Pan = pan;
aSource->DopplerFactor = doppler;
aSource->Volume = volume;
aSource->MinDistance = minDistance;
aSource->Attenuation = attenuation;
aSource->Channels = aFormat.nChannels;
aSource->Position = position;
aSource->Orientation = orientation;
aSource->Velocity = Vector3::Zero;
hr = aSource->Voice->SetVolume(volume);
XAUDIO2_CHECK_ERROR(SetVolume);
source->Restore();
return sourceID;
}
void AudioBackendXAudio2::Source_OnRemove(AudioSource* source)
void AudioBackendXAudio2::Source_Remove(uint32 sourceID)
{
ScopeLock lock(XAudio2::Locker);
source->Cleanup();
auto aSource = XAudio2::GetSource(sourceID);
if (!aSource)
return;
// Free source
if (aSource->Voice)
{
aSource->Voice->DestroyVoice();
}
aSource->Init();
}
void AudioBackendXAudio2::Source_VelocityChanged(AudioSource* source)
void AudioBackendXAudio2::Source_VelocityChanged(uint32 sourceID, const Vector3& velocity)
{
auto aSource = XAudio2::GetSource(source);
auto aSource = XAudio2::GetSource(sourceID);
if (aSource)
{
aSource->UpdateVelocity(source);
aSource->Velocity = velocity;
aSource->IsDirty = true;
}
}
void AudioBackendXAudio2::Source_TransformChanged(AudioSource* source)
void AudioBackendXAudio2::Source_TransformChanged(uint32 sourceID, const Vector3& position, const Quaternion& orientation)
{
auto aSource = XAudio2::GetSource(source);
auto aSource = XAudio2::GetSource(sourceID);
if (aSource)
{
aSource->UpdateTransform(source);
aSource->Position = position;
aSource->Orientation = orientation;
aSource->IsDirty = true;
}
}
void AudioBackendXAudio2::Source_VolumeChanged(AudioSource* source)
void AudioBackendXAudio2::Source_VolumeChanged(uint32 sourceID, float volume)
{
auto aSource = XAudio2::GetSource(source);
auto aSource = XAudio2::GetSource(sourceID);
if (aSource && aSource->Voice)
{
aSource->Volume = source->GetVolume();
const HRESULT hr = aSource->Voice->SetVolume(source->GetVolume());
aSource->Volume = volume;
const HRESULT hr = aSource->Voice->SetVolume(volume);
XAUDIO2_CHECK_ERROR(SetVolume);
}
}
void AudioBackendXAudio2::Source_PitchChanged(AudioSource* source)
void AudioBackendXAudio2::Source_PitchChanged(uint32 sourceID, float pitch)
{
auto aSource = XAudio2::GetSource(source);
auto aSource = XAudio2::GetSource(sourceID);
if (aSource)
{
aSource->Pitch = source->GetPitch();
aSource->Pitch = pitch;
aSource->IsDirty = true;
}
}
void AudioBackendXAudio2::Source_PanChanged(AudioSource* source)
void AudioBackendXAudio2::Source_PanChanged(uint32 sourceID, float pan)
{
auto aSource = XAudio2::GetSource(source);
auto aSource = XAudio2::GetSource(sourceID);
if (aSource)
{
aSource->Pan = source->GetPan();
aSource->Pan = pan;
aSource->IsDirty = true;
}
}
void AudioBackendXAudio2::Source_IsLoopingChanged(AudioSource* source)
void AudioBackendXAudio2::Source_IsLoopingChanged(uint32 sourceID, bool loop)
{
auto aSource = XAudio2::GetSource(source);
ScopeLock lock(XAudio2::Locker);
auto aSource = XAudio2::GetSource(sourceID);
if (!aSource || !aSource->Voice)
return;
aSource->IsLoop = loop;
// Skip if has no buffers (waiting for data or sth)
XAUDIO2_VOICE_STATE state;
@@ -382,15 +380,12 @@ void AudioBackendXAudio2::Source_IsLoopingChanged(AudioSource* source)
if (state.BuffersQueued == 0)
return;
// Looping is defined during buffer submission so reset source buffer (this is called only for non-streamable sources that ue single buffer)
XAudio2::Locker.Lock();
const uint32 bufferId = source->Clip->Buffers[0];
XAudio2::Buffer* aBuffer = XAudio2::Buffers[bufferId - 1];
XAudio2::Locker.Unlock();
// Looping is defined during buffer submission so reset source buffer (this is called only for non-streamable sources that use a single buffer)
const uint32 bufferID = aSource->LastBufferID;
XAudio2::Buffer* aBuffer = XAudio2::Buffers[bufferID - 1];
HRESULT hr;
const bool isPlaying = source->IsActuallyPlayingSth();
const bool isPlaying = aSource->IsPlaying;
if (isPlaying)
{
hr = aSource->Voice->Stop();
@@ -406,7 +401,7 @@ void AudioBackendXAudio2::Source_IsLoopingChanged(AudioSource* source)
XAUDIO2_BUFFER buffer = { 0 };
buffer.pContext = aBuffer;
buffer.Flags = XAUDIO2_END_OF_STREAM;
if (source->GetIsLooping())
if (loop)
buffer.LoopCount = XAUDIO2_LOOP_INFINITE;
// Restore play position
@@ -415,7 +410,7 @@ void AudioBackendXAudio2::Source_IsLoopingChanged(AudioSource* source)
buffer.PlayLength = totalSamples - buffer.PlayBegin;
aSource->StartTimeForQueueBuffer = 0;
XAudio2::QueueBuffer(aSource, source, bufferId, buffer);
XAudio2::QueueBuffer(aSource, bufferID, buffer);
if (isPlaying)
{
@@ -424,48 +419,22 @@ void AudioBackendXAudio2::Source_IsLoopingChanged(AudioSource* source)
}
}
void AudioBackendXAudio2::Source_SpatialSetupChanged(AudioSource* source)
void AudioBackendXAudio2::Source_SpatialSetupChanged(uint32 sourceID, bool spatial, float attenuation, float minDistance, float doppler)
{
auto aSource = XAudio2::GetSource(source);
auto aSource = XAudio2::GetSource(sourceID);
if (aSource)
{
aSource->Is3D = source->Is3D();
aSource->MinDistance = source->GetMinDistance();
aSource->Attenuation = source->GetAttenuation();
aSource->DopplerFactor = source->GetDopplerFactor();
aSource->Is3D = spatial;
aSource->MinDistance = minDistance;
aSource->Attenuation = attenuation;
aSource->DopplerFactor = doppler;
aSource->IsDirty = true;
}
}
void AudioBackendXAudio2::Source_ClipLoaded(AudioSource* source)
void AudioBackendXAudio2::Source_Play(uint32 sourceID)
{
ScopeLock lock(XAudio2::Locker);
auto aSource = XAudio2::GetSource(source);
if (!aSource)
{
// Register source if clip was missing
Source_OnAdd(source);
}
}
void AudioBackendXAudio2::Source_Cleanup(AudioSource* source)
{
ScopeLock lock(XAudio2::Locker);
auto aSource = XAudio2::GetSource(source);
if (!aSource)
return;
// Free source
if (aSource->Voice)
{
aSource->Voice->DestroyVoice();
}
aSource->Init();
}
void AudioBackendXAudio2::Source_Play(AudioSource* source)
{
auto aSource = XAudio2::GetSource(source);
auto aSource = XAudio2::GetSource(sourceID);
if (aSource && aSource->Voice && !aSource->IsPlaying)
{
// Play
@@ -475,9 +444,9 @@ void AudioBackendXAudio2::Source_Play(AudioSource* source)
}
}
void AudioBackendXAudio2::Source_Pause(AudioSource* source)
void AudioBackendXAudio2::Source_Pause(uint32 sourceID)
{
auto aSource = XAudio2::GetSource(source);
auto aSource = XAudio2::GetSource(sourceID);
if (aSource && aSource->Voice && aSource->IsPlaying)
{
// Pause
@@ -487,9 +456,9 @@ void AudioBackendXAudio2::Source_Pause(AudioSource* source)
}
}
void AudioBackendXAudio2::Source_Stop(AudioSource* source)
void AudioBackendXAudio2::Source_Stop(uint32 sourceID)
{
auto aSource = XAudio2::GetSource(source);
auto aSource = XAudio2::GetSource(sourceID);
if (aSource && aSource->Voice)
{
aSource->StartTimeForQueueBuffer = 0.0f;
@@ -509,9 +478,9 @@ void AudioBackendXAudio2::Source_Stop(AudioSource* source)
}
}
void AudioBackendXAudio2::Source_SetCurrentBufferTime(AudioSource* source, float value)
void AudioBackendXAudio2::Source_SetCurrentBufferTime(uint32 sourceID, float value)
{
const auto aSource = XAudio2::GetSource(source);
const auto aSource = XAudio2::GetSource(sourceID);
if (aSource)
{
// Store start time so next buffer submitted will start from here (assumes audio is stopped)
@@ -519,60 +488,63 @@ void AudioBackendXAudio2::Source_SetCurrentBufferTime(AudioSource* source, float
}
}
float AudioBackendXAudio2::Source_GetCurrentBufferTime(const AudioSource* source)
float AudioBackendXAudio2::Source_GetCurrentBufferTime(uint32 sourceID)
{
float time = 0;
auto aSource = XAudio2::GetSource(source);
auto aSource = XAudio2::GetSource(sourceID);
if (aSource)
{
ASSERT(source->Clip && source->Clip->IsLoaded());
const auto& clipInfo = source->Clip->AudioHeader.Info;
const auto& clipInfo = aSource->Info;
XAUDIO2_VOICE_STATE state;
aSource->Voice->GetState(&state);
const uint32 numChannels = clipInfo.NumChannels;
const uint32 totalSamples = clipInfo.NumSamples / numChannels;
const uint32 totalSamples = clipInfo.NumSamples / clipInfo.NumChannels;
const uint32 sampleRate = clipInfo.SampleRate; // / clipInfo.NumChannels;
state.SamplesPlayed -= aSource->LastBufferStartSamplesPlayed % totalSamples; // Offset by the last buffer start to get time relative to its begin
time = aSource->LastBufferStartTime + (state.SamplesPlayed % totalSamples) / static_cast<float>(Math::Max(1U, sampleRate));
uint64 lastBufferStartSamplesPlayed = aSource->LastBufferStartSamplesPlayed;
if (totalSamples > 0)
lastBufferStartSamplesPlayed %= totalSamples;
state.SamplesPlayed -= lastBufferStartSamplesPlayed % totalSamples; // Offset by the last buffer start to get time relative to its begin
if (totalSamples > 0)
state.SamplesPlayed %= totalSamples;
time = aSource->LastBufferStartTime + state.SamplesPlayed / static_cast<float>(Math::Max(1U, sampleRate));
}
return time;
}
void AudioBackendXAudio2::Source_SetNonStreamingBuffer(AudioSource* source)
void AudioBackendXAudio2::Source_SetNonStreamingBuffer(uint32 sourceID, uint32 bufferID)
{
auto aSource = XAudio2::GetSource(source);
auto aSource = XAudio2::GetSource(sourceID);
if (!aSource)
return;
aSource->LastBufferID = bufferID; // Use for looping change
XAudio2::Locker.Lock();
const uint32 bufferId = source->Clip->Buffers[0];
XAudio2::Buffer* aBuffer = XAudio2::Buffers[bufferId - 1];
XAudio2::Buffer* aBuffer = XAudio2::Buffers[bufferID - 1];
XAudio2::Locker.Unlock();
XAUDIO2_BUFFER buffer = { 0 };
buffer.pContext = aBuffer;
buffer.Flags = XAUDIO2_END_OF_STREAM;
if (source->GetIsLooping())
if (aSource->IsLoop)
buffer.LoopCount = XAUDIO2_LOOP_INFINITE;
// Queue single buffer
XAudio2::QueueBuffer(aSource, source, bufferId, buffer);
XAudio2::QueueBuffer(aSource, bufferID, buffer);
}
void AudioBackendXAudio2::Source_GetProcessedBuffersCount(AudioSource* source, int32& processedBuffersCount)
void AudioBackendXAudio2::Source_GetProcessedBuffersCount(uint32 sourceID, int32& processedBuffersCount)
{
processedBuffersCount = 0;
auto aSource = XAudio2::GetSource(source);
auto aSource = XAudio2::GetSource(sourceID);
if (aSource && aSource->Voice)
{
processedBuffersCount = aSource->BuffersProcessed;
}
}
void AudioBackendXAudio2::Source_GetQueuedBuffersCount(AudioSource* source, int32& queuedBuffersCount)
void AudioBackendXAudio2::Source_GetQueuedBuffersCount(uint32 sourceID, int32& queuedBuffersCount)
{
queuedBuffersCount = 0;
auto aSource = XAudio2::GetSource(source);
auto aSource = XAudio2::GetSource(sourceID);
if (aSource && aSource->Voice)
{
XAUDIO2_VOICE_STATE state;
@@ -581,23 +553,24 @@ void AudioBackendXAudio2::Source_GetQueuedBuffersCount(AudioSource* source, int3
}
}
void AudioBackendXAudio2::Source_QueueBuffer(AudioSource* source, uint32 bufferId)
void AudioBackendXAudio2::Source_QueueBuffer(uint32 sourceID, uint32 bufferID)
{
auto aSource = XAudio2::GetSource(source);
auto aSource = XAudio2::GetSource(sourceID);
if (!aSource)
return;
aSource->LastBufferID = bufferID; // Use for looping change
XAudio2::Buffer* aBuffer = XAudio2::Buffers[bufferId - 1];
XAudio2::Buffer* aBuffer = XAudio2::Buffers[bufferID - 1];
XAUDIO2_BUFFER buffer = { 0 };
buffer.pContext = aBuffer;
XAudio2::QueueBuffer(aSource, source, bufferId, buffer);
XAudio2::QueueBuffer(aSource, bufferID, buffer);
}
void AudioBackendXAudio2::Source_DequeueProcessedBuffers(AudioSource* source)
void AudioBackendXAudio2::Source_DequeueProcessedBuffers(uint32 sourceID)
{
auto aSource = XAudio2::GetSource(source);
auto aSource = XAudio2::GetSource(sourceID);
if (aSource && aSource->Voice)
{
const HRESULT hr = aSource->Voice->FlushSourceBuffers();
@@ -608,7 +581,7 @@ void AudioBackendXAudio2::Source_DequeueProcessedBuffers(AudioSource* source)
uint32 AudioBackendXAudio2::Buffer_Create()
{
uint32 bufferId;
uint32 bufferID;
ScopeLock lock(XAudio2::Locker);
// Get first free buffer slot
@@ -619,7 +592,7 @@ uint32 AudioBackendXAudio2::Buffer_Create()
{
aBuffer = New<XAudio2::Buffer>();
XAudio2::Buffers[i] = aBuffer;
bufferId = i + 1;
bufferID = i + 1;
break;
}
}
@@ -628,28 +601,28 @@ uint32 AudioBackendXAudio2::Buffer_Create()
// Add new slot
aBuffer = New<XAudio2::Buffer>();
XAudio2::Buffers.Add(aBuffer);
bufferId = XAudio2::Buffers.Count();
bufferID = XAudio2::Buffers.Count();
}
aBuffer->Data.Resize(0);
return bufferId;
return bufferID;
}
void AudioBackendXAudio2::Buffer_Delete(uint32 bufferId)
void AudioBackendXAudio2::Buffer_Delete(uint32 bufferID)
{
ScopeLock lock(XAudio2::Locker);
XAudio2::Buffer*& aBuffer = XAudio2::Buffers[bufferId - 1];
XAudio2::Buffer*& aBuffer = XAudio2::Buffers[bufferID - 1];
aBuffer->Data.Resize(0);
Delete(aBuffer);
aBuffer = nullptr;
}
void AudioBackendXAudio2::Buffer_Write(uint32 bufferId, byte* samples, const AudioDataInfo& info)
void AudioBackendXAudio2::Buffer_Write(uint32 bufferID, byte* samples, const AudioDataInfo& info)
{
CHECK(info.NumChannels <= MAX_INPUT_CHANNELS);
XAudio2::Locker.Lock();
XAudio2::Buffer* aBuffer = XAudio2::Buffers[bufferId - 1];
XAudio2::Buffer* aBuffer = XAudio2::Buffers[bufferID - 1];
XAudio2::Locker.Unlock();
const uint32 samplesLength = info.NumSamples * info.BitDepth / 8;
@@ -735,7 +708,6 @@ bool AudioBackendXAudio2::Base_Init()
void AudioBackendXAudio2::Base_Update()
{
// Update dirty voices
const auto listener = XAudio2::GetListener();
float outputMatrix[MAX_CHANNELS_MATRIX_SIZE];
for (int32 i = 0; i < XAudio2::Sources.Count(); i++)
{
@@ -743,7 +715,7 @@ void AudioBackendXAudio2::Base_Update()
if (source.IsFree() || !(source.IsDirty || XAudio2::ForceDirty))
continue;
auto mix = AudioBackendTools::CalculateSoundMix(XAudio2::Settings, *listener, source, XAudio2::Channels);
auto mix = AudioBackendTools::CalculateSoundMix(XAudio2::Settings, XAudio2::Listener, source, XAudio2::Channels);
mix.VolumeIntoChannels();
AudioBackendTools::MapChannels(source.Channels, XAudio2::Channels, mix.Channels, outputMatrix);

View File

@@ -17,30 +17,28 @@ public:
void Listener_VelocityChanged(const Vector3& velocity) override;
void Listener_TransformChanged(const Vector3& position, const Quaternion& orientation) override;
void Listener_ReinitializeAll() override;
void Source_OnAdd(AudioSource* source) override;
void Source_OnRemove(AudioSource* source) override;
void Source_VelocityChanged(AudioSource* source) override;
void Source_TransformChanged(AudioSource* source) override;
void Source_VolumeChanged(AudioSource* source) override;
void Source_PitchChanged(AudioSource* source) override;
void Source_PanChanged(AudioSource* source) override;
void Source_IsLoopingChanged(AudioSource* source) override;
void Source_SpatialSetupChanged(AudioSource* source) override;
void Source_ClipLoaded(AudioSource* source) override;
void Source_Cleanup(AudioSource* source) override;
void Source_Play(AudioSource* source) override;
void Source_Pause(AudioSource* source) override;
void Source_Stop(AudioSource* source) override;
void Source_SetCurrentBufferTime(AudioSource* source, float value) override;
float Source_GetCurrentBufferTime(const AudioSource* source) override;
void Source_SetNonStreamingBuffer(AudioSource* source) override;
void Source_GetProcessedBuffersCount(AudioSource* source, int32& processedBuffersCount) override;
void Source_GetQueuedBuffersCount(AudioSource* source, int32& queuedBuffersCount) override;
void Source_QueueBuffer(AudioSource* source, uint32 bufferId) override;
void Source_DequeueProcessedBuffers(AudioSource* source) override;
uint32 Source_Add(const AudioDataInfo& format, const Vector3& position, const Quaternion& orientation, float volume, float pitch, float pan, bool loop, bool spatial, float attenuation, float minDistance, float doppler) override;
void Source_Remove(uint32 sourceID) override;
void Source_VelocityChanged(uint32 sourceID, const Vector3& velocity) override;
void Source_TransformChanged(uint32 sourceID, const Vector3& position, const Quaternion& orientation) override;
void Source_VolumeChanged(uint32 sourceID, float volume) override;
void Source_PitchChanged(uint32 sourceID, float pitch) override;
void Source_PanChanged(uint32 sourceID, float pan) override;
void Source_IsLoopingChanged(uint32 sourceID, bool loop) override;
void Source_SpatialSetupChanged(uint32 sourceID, bool spatial, float attenuation, float minDistance, float doppler) override;
void Source_Play(uint32 sourceID) override;
void Source_Pause(uint32 sourceID) override;
void Source_Stop(uint32 sourceID) override;
void Source_SetCurrentBufferTime(uint32 sourceID, float value) override;
float Source_GetCurrentBufferTime(uint32 sourceID) override;
void Source_SetNonStreamingBuffer(uint32 sourceID, uint32 bufferID) override;
void Source_GetProcessedBuffersCount(uint32 sourceID, int32& processedBuffersCount) override;
void Source_GetQueuedBuffersCount(uint32 sourceID, int32& queuedBuffersCount) override;
void Source_QueueBuffer(uint32 sourceID, uint32 bufferID) override;
void Source_DequeueProcessedBuffers(uint32 sourceID) override;
uint32 Buffer_Create() override;
void Buffer_Delete(uint32 bufferId) override;
void Buffer_Write(uint32 bufferId, byte* samples, const AudioDataInfo& info) override;
void Buffer_Delete(uint32 bufferID) override;
void Buffer_Write(uint32 bufferID, byte* samples, const AudioDataInfo& info) override;
const Char* Base_Name() override;
FeatureFlags Base_Features() override;
void Base_OnActiveDeviceChanged() override;