diff --git a/Source/Engine/Audio/Audio.cpp b/Source/Engine/Audio/Audio.cpp index 25b6f39e4..e279f4d44 100644 --- a/Source/Engine/Audio/Audio.cpp +++ b/Source/Engine/Audio/Audio.cpp @@ -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"); diff --git a/Source/Engine/Audio/Audio.h b/Source/Engine/Audio/Audio.h index a074fefda..5a82a6da8 100644 --- a/Source/Engine/Audio/Audio.h +++ b/Source/Engine/Audio/Audio.h @@ -97,11 +97,4 @@ public: /// /// The value. 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); }; diff --git a/Source/Engine/Audio/AudioBackend.h b/Source/Engine/Audio/AudioBackend.h index e7424e1ad..ee94e21a3 100644 --- a/Source/Engine/Audio/AudioBackend.h +++ b/Source/Engine/Audio/AudioBackend.h @@ -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); } }; diff --git a/Source/Engine/Audio/AudioClip.cpp b/Source/Engine/Audio/AudioClip.cpp index 0abcc5479..93e85f800 100644 --- a/Source/Engine/Audio/AudioClip.cpp +++ b/Source/Engine/Audio/AudioClip.cpp @@ -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; } diff --git a/Source/Engine/Audio/AudioListener.cpp b/Source/Engine/Audio/AudioListener.cpp index ce5243869..48cac4205 100644 --- a/Source/Engine/Audio/AudioListener.cpp +++ b/Source/Engine/Audio/AudioListener.cpp @@ -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(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(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(); diff --git a/Source/Engine/Audio/AudioSource.cpp b/Source/Engine/Audio/AudioSource.cpp index dbe57e150..b9e0ed92d 100644 --- a/Source/Engine/Audio/AudioSource.cpp +++ b/Source/Engine/Audio/AudioSource.cpp @@ -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(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(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); } } diff --git a/Source/Engine/Audio/AudioSource.h b/Source/Engine/Audio/AudioSource.h index f94e04fb9..682c07563 100644 --- a/Source/Engine/Audio/AudioSource.h +++ b/Source/Engine/Audio/AudioSource.h @@ -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: /// API_PROPERTY() bool UseStreaming() const; - /// - /// 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). - /// - void Restore(); - public: /// /// 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: /// void RequestStreamingBuffersUpdate(); - /// - /// Cleanups the cached data. Called by the Audio manager. - /// - void Cleanup(); - private: void OnClipChanged(); void OnClipLoaded(); - /// - /// Sets the single buffer from the audio clip that is not using dynamic streaming - /// - void SetNonStreamingBuffer(); - /// /// Plays the audio source. Should have buffer(s) binded before. /// diff --git a/Source/Engine/Audio/Config.h b/Source/Engine/Audio/Config.h index e824b8e0f..764468ae5 100644 --- a/Source/Engine/Audio/Config.h +++ b/Source/Engine/Audio/Config.h @@ -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 diff --git a/Source/Engine/Audio/None/AudioBackendNone.cpp b/Source/Engine/Audio/None/AudioBackendNone.cpp index a28fb91b2..4bf2df88f 100644 --- a/Source/Engine/Audio/None/AudioBackendNone.cpp +++ b/Source/Engine/Audio/None/AudioBackendNone.cpp @@ -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) { } diff --git a/Source/Engine/Audio/None/AudioBackendNone.h b/Source/Engine/Audio/None/AudioBackendNone.h index 5a28331b6..6ceb3149e 100644 --- a/Source/Engine/Audio/None/AudioBackendNone.h +++ b/Source/Engine/Audio/None/AudioBackendNone.h @@ -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; diff --git a/Source/Engine/Audio/OpenAL/AudioBackendOAL.cpp b/Source/Engine/Audio/OpenAL/AudioBackendOAL.cpp index 77d8f43c9..d29e3a31b 100644 --- a/Source/Engine/Audio/OpenAL/AudioBackendOAL.cpp +++ b/Source/Engine/Audio/OpenAL/AudioBackendOAL.cpp @@ -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 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& 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 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(Math::Max(1U, clipInfo.SampleRate)); + if (totalSamples > 0) + samplesPlayed %= totalSamples; + const float time = samplesPlayed / static_cast(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 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) diff --git a/Source/Engine/Audio/OpenAL/AudioBackendOAL.h b/Source/Engine/Audio/OpenAL/AudioBackendOAL.h index 3375251a4..258f5e967 100644 --- a/Source/Engine/Audio/OpenAL/AudioBackendOAL.h +++ b/Source/Engine/Audio/OpenAL/AudioBackendOAL.h @@ -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; diff --git a/Source/Engine/Audio/XAudio2/AudioBackendXAudio2.cpp b/Source/Engine/Audio/XAudio2/AudioBackendXAudio2.cpp index 4f24013e1..4c6f93947 100644 --- a/Source/Engine/Audio/XAudio2/AudioBackendXAudio2.cpp +++ b/Source/Engine/Audio/XAudio2/AudioBackendXAudio2.cpp @@ -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 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(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(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::Buffers[i] = aBuffer; - bufferId = i + 1; + bufferID = i + 1; break; } } @@ -628,28 +601,28 @@ uint32 AudioBackendXAudio2::Buffer_Create() // Add new slot aBuffer = New(); 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); diff --git a/Source/Engine/Audio/XAudio2/AudioBackendXAudio2.h b/Source/Engine/Audio/XAudio2/AudioBackendXAudio2.h index 073dbfdc3..56c79bb69 100644 --- a/Source/Engine/Audio/XAudio2/AudioBackendXAudio2.h +++ b/Source/Engine/Audio/XAudio2/AudioBackendXAudio2.h @@ -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;