From df0e764aab636dab85f5833d26ff86cf817a076b Mon Sep 17 00:00:00 2001 From: Ken Johnson Date: Sun, 5 Nov 2023 21:44:35 -0500 Subject: [PATCH 01/40] Progress commit: Compiling, untested - now submitting individual buffers to OpenAL --- audio/AudioMixer.cpp | 41 ++ audio/AudioService.cpp | 484 ------------------ audio/CMakeLists.txt | 8 +- audio/OpenAL/OpenALAudioProvider.cpp | 92 ++++ audio/include/NovelRT/Audio/Audio.hpp | 31 -- audio/include/NovelRT/Audio/AudioMixer.hpp | 25 + audio/include/NovelRT/Audio/AudioService.hpp | 75 --- .../include/NovelRT/Audio/IAudioProvider.hpp | 23 + .../Audio/OpenAL/OpenALAudioProvider.hpp | 32 ++ .../ResourceManagement/AudioMetadata.h | 4 - 10 files changed, 218 insertions(+), 597 deletions(-) create mode 100644 audio/AudioMixer.cpp delete mode 100644 audio/AudioService.cpp create mode 100644 audio/OpenAL/OpenALAudioProvider.cpp delete mode 100644 audio/include/NovelRT/Audio/Audio.hpp create mode 100644 audio/include/NovelRT/Audio/AudioMixer.hpp delete mode 100644 audio/include/NovelRT/Audio/AudioService.hpp create mode 100644 audio/include/NovelRT/Audio/IAudioProvider.hpp create mode 100644 audio/include/NovelRT/Audio/OpenAL/OpenALAudioProvider.hpp diff --git a/audio/AudioMixer.cpp b/audio/AudioMixer.cpp new file mode 100644 index 000000000..29b5d58eb --- /dev/null +++ b/audio/AudioMixer.cpp @@ -0,0 +1,41 @@ +// Copyright © Matt Jones and Contributors. Licensed under the MIT Licence (MIT). See LICENCE.md in the repository root +// for more information. +#include + +//Conditional +#include + +namespace NovelRT::Audio +{ + void AudioMixer::Initialise() + { + _audioProvider = std::make_unique(); + _audioProvider->OpenOutputStream(); + } + + void AudioMixer::SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer) + { + _audioProvider->SubmitAudioBuffer(buffer); + } + + void AudioMixer::PlayOutputStream() + { + _audioProvider->PlayOutputStream(); + } + + void AudioMixer::StopOutputStream() + { + _audioProvider->StopOutputStream(); + } + + void AudioMixer::TearDown() + { + _audioProvider->CloseOutputStream(); + _audioProvider.reset(); + } + + AudioMixer::~AudioMixer() + { + TearDown(); + } +} diff --git a/audio/AudioService.cpp b/audio/AudioService.cpp deleted file mode 100644 index 9c02d0526..000000000 --- a/audio/AudioService.cpp +++ /dev/null @@ -1,484 +0,0 @@ -// Copyright © Matt Jones and Contributors. Licensed under the MIT Licence (MIT). See LICENCE.md in the repository root -// for more information. - -#include - -namespace NovelRT::Audio -{ - AudioService::AudioService() - : _device(Utilities::Lazy>( - std::function([this] { - auto device = alcOpenDevice((_deviceName.empty()) ? nullptr : _deviceName.c_str()); - if (!device) - { - std::string error = GetALError(); - _logger.logError("OpenAL device creation failed! {}", error); - throw Exceptions::InitialisationFailureException( - "OpenAL failed to create an audio device! Aborting...", error); - } - return device; - }), - [](auto x) { alcCloseDevice(x); })), - _context(Utilities::Lazy>( - std::function([this] { - auto context = alcCreateContext(_device.getActual(), nullptr); - alcMakeContextCurrent(context); - isInitialised = true; - _deviceName = alcGetString(_device.getActual(), ALC_DEVICE_SPECIFIER); - _logger.logInfo("OpenAL Initialized on device: {}", _deviceName); - return context; - }), - [](auto x) { - alcMakeContextCurrent(nullptr); - alcDestroyContext(x); - })), - _logger(Utilities::Misc::CONSOLE_LOG_AUDIO), - _manualLoad(false), - _musicSource(), - _musicSourceState(0), - _musicStopRequested(false), - _musicLoopAmount(0), - _soundLoopAmount(0), - _soundSourceState(0), - _soundStorage(), - _bufferStorage(), - isInitialised(false) - { - } - - bool AudioService::InitializeAudio() - { - _device.getActual(); - _context.getActual(); - alGenSources(1, &_musicSource); - alSourcef(_musicSource, AL_GAIN, 0.75f); - alSourcef(_musicSource, AL_PITCH, _pitch); - - return isInitialised; - } - - ALuint AudioService::BufferAudioFrameData(NovelRT::Utilities::Misc::Span audioFrameData, - int32_t channelCount, - int32_t sampleRate) - { - ALuint buffer; - alGenBuffers(1, &buffer); - - if (buffer == _noBuffer) - { - return _noBuffer; - } - - alBufferData(buffer, channelCount == 1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16, audioFrameData.data(), - static_cast(audioFrameData.size() * sizeof(int16_t)), sampleRate); - return buffer; - } - - /*Note: Due to the current design, this will currently block the thread it is being called on. - If it is called on the main thread, please do all loading of audio files at the start of - the engine (after NovelRunner has been created). - */ - std::vector::iterator AudioService::LoadMusic(NovelRT::Utilities::Misc::Span audioFrameData, - int32_t channelCount, - int32_t sampleRate) - { - if (!isInitialised) - { - _logger.logError("Cannot load new audio into memory while the service is uninitialised! Aborting..."); - throw NovelRT::Exceptions::NotInitialisedException( - "AudioService::load", "You cannot load new audio when the service is not initialised."); - } - - auto newBuffer = BufferAudioFrameData(audioFrameData, channelCount, sampleRate); - - // Sorry Matt, nullptr types are incompatible to ALuint according to VS. - if (newBuffer == _noBuffer) - { - _logger.logWarning( - "Could not buffer provided audio data. Please verify the resource was loaded correctly."); - return _music.end(); - } - - auto it = std::find(_music.begin(), _music.end(), newBuffer); - if (it != _music.end()) - { - alDeleteBuffers(1, &newBuffer); - return it; - } - else - { - _music.push_back(newBuffer); - _bufferStorage.push_back(newBuffer); - it = std::find(_music.begin(), _music.end(), newBuffer); - return it; - } - } - - void AudioService::SetSoundVolume(ALuint source, float value) - { - if (!isInitialised) - { - _logger.logError( - "Cannot change the volume of a nonexistent sound! the service is uninitialised! Aborting..."); - throw NovelRT::Exceptions::NotInitialisedException( - "AudioService::SetSoundVolume", - "You cannot modify a sound source when the AudioService is not initialised."); - } - - if (value > 1.0f) - { - alSourcef(source, AL_GAIN, 1.0f); - } - else if (value <= 0.0f) - { - alSourcef(source, AL_GAIN, 0.0f); - } - else - { - alSourcef(source, AL_GAIN, value); - } - } - - // Switched to using two floats - for some reason VS complained when trying to use Maths::GeoVector2 here... - // This also has no effect if the buffer is more than one channel (not Mono) - void AudioService::SetSoundPosition(ALuint source, float posX, float posY) - { - if (!isInitialised) - { - _logger.logError( - "Cannot move audio position on a nonexistent sound! The service is uninitialised! Aborting..."); - throw NovelRT::Exceptions::NotInitialisedException( - "AudioService::StopSound", "You cannot stop a sound when the AudioService is not initialised."); - } - - alSource3f(source, AL_POSITION, posX, posY, 0.0f); - } - - void AudioService::ResumeMusic() - { - if (!isInitialised) - { - _logger.logError( - "Cannot change the volume of a nonexistent sound! The service is uninitialised! Aborting..."); - throw NovelRT::Exceptions::NotInitialisedException( - "AudioService::SetSoundVolume", - "You cannot modify a sound source when the AudioService is not initialised."); - } - - alSourcePlay(_musicSource); - _musicStopRequested = false; - } - - void AudioService::PlayMusic(std::vector::iterator handle, int32_t loops) - { - if (!isInitialised) - { - _logger.logError("Cannot play audio while the service is uninitialised! Aborting..."); - throw NovelRT::Exceptions::NotInitialisedException( - "AudioService::playMusic", "You cannot play a sound when the AudioService is not initialised."); - } - - if (handle == _music.end()) - { - _logger.logWarning("Cannot play the requested sound - it may have been deleted or not loaded properly."); - return; - } - - alGetSourcei(_musicSource, AL_SOURCE_STATE, &_musicSourceState); - if (_musicSourceState == AL_PLAYING) - { - alSourceStop(_musicSource); - alGetSourcei(_musicSource, AL_SOURCE_STATE, &_musicSourceState); - } - alSourcei(_musicSource, AL_BUFFER, static_cast(*handle)); - if (loops == -1 || loops > 0) - { - _musicLoopAmount = loops; - alSourcei(_musicSource, AL_LOOPING, AL_TRUE); - } - else - { - alSourcei(_musicSource, AL_LOOPING, AL_FALSE); - } - alSourcePlay(_musicSource); - _musicStopRequested = false; - } - - void AudioService::PauseMusic() - { - if (!isInitialised) - { - _logger.logError("Cannot pause audio while the service is uninitialised! Aborting..."); - throw NovelRT::Exceptions::NotInitialisedException( - "AudioService::PauseMusic", "You cannot pause a sound when the AudioService is not initialised."); - } - - _musicStopRequested = true; - alSourcePause(_musicSource); - } - - void AudioService::StopMusic() - { - if (!isInitialised) - { - _logger.logError("Cannot stop audio while the service is uninitialised! Aborting..."); - throw NovelRT::Exceptions::NotInitialisedException( - "AudioService::StopMusic", "You cannot stop a sound when the AudioService is not initialised."); - } - - _musicStopRequested = true; - alSourceStop(_musicSource); - } - - void AudioService::SetMusicVolume(float value) - { - if (!isInitialised) - { - _logger.logError("Cannot modify audio while the service is uninitialised! Aborting..."); - throw NovelRT::Exceptions::NotInitialisedException( - "AudioService::SetMusicVolume", "You cannot modify a sound when the AudioService is not initialised."); - } - - if (value > 1.0f) - { - alSourcef(_musicSource, AL_GAIN, 1.0f); - } - else if (value <= 0.0f) - { - alSourcef(_musicSource, AL_GAIN, 0.0f); - } - else - { - alSourcef(_musicSource, AL_GAIN, value); - } - } - - void AudioService::CheckSources() - { - // Changing the init check as I don't want this to kill the Runner. - if (isInitialised) - { - - int32_t musicLoop = 0; - int32_t soundLoop = 0; - for (auto sound : _soundStorage) - { - alGetSourcei(sound, AL_LOOPING, &soundLoop); - if (soundLoop == AL_TRUE) - { - alGetSourcei(sound, AL_SOURCE_STATE, &_soundSourceState); - if (_soundLoopAmount > 0) - { - _soundLoopAmount--; - if (_soundLoopAmount == 0) - { - alSourcei(sound, AL_LOOPING, AL_FALSE); - } - } - } - } - - alGetSourcei(_musicSource, AL_LOOPING, &musicLoop); - - if (musicLoop == AL_TRUE) - { - alGetSourcei(_musicSource, AL_SOURCE_STATE, &_musicSourceState); - if (_musicLoopAmount > 0 && !_musicStopRequested) - { - _musicLoopAmount--; - if (_musicLoopAmount == 0) - { - alSourcei(_musicSource, AL_LOOPING, AL_FALSE); - } - } - } - } - } - - std::string AudioService::GetALError() - { - auto err = alGetError(); - switch (err) - { - case AL_INVALID_NAME: - { - return std::string("A bad ID or name was passed to the OpenAL function."); - } - case AL_INVALID_ENUM: - { - return std::string("An invalid enum was passed to an OpenAL function."); - } - case AL_INVALID_VALUE: - { - return std::string("An invalid value was passed to an OpenAL function."); - } - case AL_INVALID_OPERATION: - { - return std::string("The requested operation is not valid."); - } - case AL_OUT_OF_MEMORY: - { - return std::string("The requested operation resulted in OpenAL running out of memory."); - } - default: - { - return std::string(""); - } - } - } - - ALuint AudioService::LoadSound(NovelRT::Utilities::Misc::Span audioFrameData, - int32_t channelCount, - int32_t sampleRate) - { - if (!isInitialised) - { - _logger.logError("Cannot load new audio into memory while the service is yolo uninitialised! Aborting..."); - throw NovelRT::Exceptions::NotInitialisedException( - "AudioService::load", "You cannot load new audio when the service is not initialised."); - } - auto newBuffer = BufferAudioFrameData(audioFrameData, channelCount, sampleRate); - - if (newBuffer == _noBuffer) - { - _logger.logWarning("Cannot play the requested sound - it may have been deleted or not loaded properly."); - return _noBuffer; - } - - _manualLoad = true; - ALuint newSource = _noBuffer; - alGenSources(1, &newSource); - alSourcef(newSource, AL_GAIN, 0.75f); - alSourcef(newSource, AL_PITCH, _pitch); - alSourcei(newSource, AL_BUFFER, static_cast(newBuffer)); - - _soundStorage.push_back(newSource); - _bufferStorage.push_back(newBuffer); - - return newSource; - } - - void AudioService::Unload(ALuint source) - { - alSourcei(source, AL_BUFFER, 0); - } - - void AudioService::PlaySound(ALuint handle, int32_t loops) - { - if (!isInitialised) - { - _logger.logError("Cannot play audio while the service is uninitialised! Aborting..."); - throw NovelRT::Exceptions::NotInitialisedException( - "AudioService::playMusic", "You cannot play a sound when the AudioService is not initialised."); - } - - if (handle == _noBuffer) - { - _logger.logError("Cannot play the requested sound - it may have been deleted or not loaded properly."); - return; - } - - if (loops == -1 || loops > 0) - { - _soundLoopAmount = loops; - alSourcei(handle, AL_LOOPING, AL_TRUE); - } - else - { - alSourcei(handle, AL_LOOPING, AL_FALSE); - } - alSourcePlay(handle); - } - - void AudioService::StopSound(ALuint handle) - { - alSourceStop(handle); - } - - bool AudioService::IsLoaded(std::vector::iterator handle) - { - return (handle != _music.end()); - } - - bool AudioService::IsLoaded(ALuint handle) - { - return (handle != _noBuffer); - } - - void AudioService::TearDown() - { - if (!_context.isCreated()) - return; - - if (_manualLoad) - { - for (auto source : _soundStorage) - { - alDeleteSources(1, &source); - } - _soundStorage.clear(); - } - - alDeleteSources(1, &_musicSource); - if (!_music.empty()) - { - _music.clear(); - } - - for (auto buffer : _bufferStorage) - { - alDeleteBuffers(1, &buffer); - } - - // were deleting the objects explicitly here to ensure they're always deleted in the right order, lest you - // summon the kraken. - Ruby - _context.reset(); - _device.reset(); - } - - AudioService::~AudioService() - { - TearDown(); - } - - bool AudioService::IsMusicPlaying() - { - alGetSourcei(_musicSource, AL_SOURCE_STATE, &_musicSourceState); - return (_musicSourceState == AL_PLAYING || _musicSourceState == AL_PAUSED); - } - - bool AudioService::IsSoundPlaying(ALuint handle) - { - alGetSourcei(handle, AL_SOURCE_STATE, &_soundSourceState); - return (_soundSourceState == AL_PLAYING || _soundSourceState == AL_PAUSED); - } - - float AudioService::GetMusicVolume() - { - if (!isInitialised) - { - _logger.logError("Cannot modify audio while the service is uninitialised! Aborting..."); - throw NovelRT::Exceptions::NotInitialisedException( - "AudioService::SetMusicVolume", "You cannot modify a sound when the AudioService is not initialised."); - } - - float result = 0.0f; - alGetSourcef(_musicSource, AL_GAIN, &result); - return result; - } - - float AudioService::GetSoundVolume(ALuint handle) - { - if (!isInitialised) - { - _logger.logError("Cannot modify audio while the service is uninitialised! Aborting..."); - throw NovelRT::Exceptions::NotInitialisedException( - "AudioService::SetMusicVolume", "You cannot modify a sound when the AudioService is not initialised."); - } - - float result = 0.0f; - alGetSourcef(handle, AL_GAIN, &result); - return result; - } - -} // namespace NovelRT::Audio diff --git a/audio/CMakeLists.txt b/audio/CMakeLists.txt index 8fdb940a5..1d873f634 100644 --- a/audio/CMakeLists.txt +++ b/audio/CMakeLists.txt @@ -1,5 +1,6 @@ add_library(NovelRT-Audio STATIC - AudioService.cpp + AudioMixer.cpp + OpenAL/OpenALAudioProvider.cpp ) target_sources(NovelRT-Audio @@ -8,8 +9,9 @@ target_sources(NovelRT-Audio TYPE HEADERS BASE_DIRS include FILES - include/NovelRT/Audio/Audio.hpp - include/NovelRT/Audio/AudioService.hpp + include/NovelRT/Audio/AudioMixer.hpp + include/NovelRT/Audio/IAudioProvider.hpp + include/NovelRT/Audio/OpenAL/OpenALAudioProvider.hpp ) set_target_properties(NovelRT-Audio diff --git a/audio/OpenAL/OpenALAudioProvider.cpp b/audio/OpenAL/OpenALAudioProvider.cpp new file mode 100644 index 000000000..2f2ed44aa --- /dev/null +++ b/audio/OpenAL/OpenALAudioProvider.cpp @@ -0,0 +1,92 @@ +// Copyright © Matt Jones and Contributors. Licensed under the MIT Licence (MIT). See LICENCE.md in the repository root +// for more information. +#include +#include + +namespace NovelRT::Audio::OpenAL +{ + OpenALAudioProvider::OpenALAudioProvider() + { + //Device and Context Init + _device = alcOpenDevice(nullptr); + if(!_device) + { + std::string error = GetALError(); + throw Exceptions::InitialisationFailureException( + "OpenAL failed to create an audio device!", error); + } + _context = alcCreateContext(_device, nullptr); + alcMakeContextCurrent(_context); + + OpenOutputStream(); + } + + void OpenALAudioProvider::Dispose() + { + CloseOutputStream(); + + + } + + void OpenALAudioProvider::OpenOutputStream() + { + alGenSources(1, &_outputSource); + alSourcef(_outputSource, AL_GAIN, 0.75f); + alSourcef(_outputSource, AL_PITCH, 1.0f); + } + + void OpenALAudioProvider::CloseOutputStream() + { + alDeleteSources(1, &_outputSource); + } + + void OpenALAudioProvider::PlayOutputStream() + { + + } + + void OpenALAudioProvider::StopOutputStream() + { + + } + + std::string OpenALAudioProvider::GetALError() + { + auto err = alGetError(); + switch (err) + { + case AL_INVALID_NAME: + { + return std::string("A bad ID or name was passed to the OpenAL function."); + } + case AL_INVALID_ENUM: + { + return std::string("An invalid enum was passed to an OpenAL function."); + } + case AL_INVALID_VALUE: + { + return std::string("An invalid value was passed to an OpenAL function."); + } + case AL_INVALID_OPERATION: + { + return std::string("The requested operation is not valid."); + } + case AL_OUT_OF_MEMORY: + { + return std::string("The requested operation resulted in OpenAL running out of memory."); + } + default: + { + return std::string(""); + } + } + } + + void OpenALAudioProvider::SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer) + { + ALuint alBuffer; + alGenBuffers(1, &alBuffer); + alBufferData(alBuffer, AL_FORMAT_STEREO16, buffer.data(), static_cast(buffer.size() * sizeof(int16_t)), 44100); + alSourcei(_outputSource, AL_BUFFER, alBuffer); + } +} diff --git a/audio/include/NovelRT/Audio/Audio.hpp b/audio/include/NovelRT/Audio/Audio.hpp deleted file mode 100644 index 79653a3aa..000000000 --- a/audio/include/NovelRT/Audio/Audio.hpp +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright © Matt Jones and Contributors. Licensed under the MIT Licence (MIT). See LICENCE.md in the repository root -// for more information. - -#ifndef NOVELRT_AUDIO_H -#define NOVELRT_AUDIO_H - -// Dependencies for Audio -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/*** - * @brief Contains audio features, such as playing audio, and managing audio resources. - */ -namespace NovelRT::Audio -{ - typedef std::vector SoundBank; - typedef std::vector MusicBank; - typedef class AudioService AudioService; -} - -// Audio Types -#include - -#endif // NOVELRT_AUDIO_H diff --git a/audio/include/NovelRT/Audio/AudioMixer.hpp b/audio/include/NovelRT/Audio/AudioMixer.hpp new file mode 100644 index 000000000..a8f2f9a23 --- /dev/null +++ b/audio/include/NovelRT/Audio/AudioMixer.hpp @@ -0,0 +1,25 @@ +// Copyright © Matt Jones and Contributors. Licensed under the MIT Licence (MIT). See LICENCE.md in the repository root +// for more information. +#pragma once + +#include +#include +#include +#include + +namespace NovelRT::Audio +{ + class AudioMixer + { + private: + std::unique_ptr _audioProvider; + void TearDown(); + + public: + void Initialise(); + void SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer); + void PlayOutputStream(); + void StopOutputStream(); + ~AudioMixer(); + }; +} diff --git a/audio/include/NovelRT/Audio/AudioService.hpp b/audio/include/NovelRT/Audio/AudioService.hpp deleted file mode 100644 index 8e4689852..000000000 --- a/audio/include/NovelRT/Audio/AudioService.hpp +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright © Matt Jones and Contributors. Licensed under the MIT Licence (MIT). See LICENCE.md in the repository root -// for more information. - -#ifndef NOVELRT_AUDIO_AUDIOSERVICE_H -#define NOVELRT_AUDIO_AUDIOSERVICE_H - -#ifndef NOVELRT_AUDIO_H -#error NovelRT does not support including types explicitly by default. Please include Audio.h instead for the Audio namespace subset. -#endif - -namespace NovelRT::Audio -{ - // TODO: This won't exist after Kenny's rewrite, not too bothered about this class. - class AudioService - { - private: - const ALuint _noBuffer = 0; - const ALfloat _pitch = 1.0f; - - Utilities::Lazy> _device; - Utilities::Lazy> _context; - std::string _deviceName; - LoggingService _logger; - bool _manualLoad; - MusicBank _music; - ALuint _musicSource; - ALint _musicSourceState; - bool _musicStopRequested; - ALint _musicLoopAmount; - ALint _soundLoopAmount; - ALint _soundSourceState; - SoundBank _soundStorage; - SoundBank _bufferStorage; - - ALuint BufferAudioFrameData(NovelRT::Utilities::Misc::Span audioFrameData, - int32_t channelCount, - int32_t sampleRate); - std::string GetALError(); - - public: - bool isInitialised; - - AudioService(); - ~AudioService(); - - bool InitializeAudio(); - std::vector::iterator LoadMusic(NovelRT::Utilities::Misc::Span audioFrameData, - int32_t channelCount, - int32_t sampleRate); - - void SetSoundVolume(ALuint source, float val); - void SetSoundPosition(ALuint source, float posX, float posY); - void ResumeMusic(); - void PlayMusic(std::vector::iterator handle, int32_t loops); - void PauseMusic(); - void StopMusic(); - void SetMusicVolume(float value); - void CheckSources(); - ALuint LoadSound(NovelRT::Utilities::Misc::Span audioFrameData, - int32_t channelCount, - int32_t sampleRate); - void Unload(ALuint handle); - void PlaySound(ALuint handle, int32_t loops); - void StopSound(ALuint handle); - void TearDown(); - [[nodiscard]] bool IsLoaded(std::vector::iterator handle); - [[nodiscard]] bool IsLoaded(ALuint handle); - [[nodiscard]] bool IsMusicPlaying(); - [[nodiscard]] bool IsSoundPlaying(ALuint handle); - [[nodiscard]] float GetMusicVolume(); - [[nodiscard]] float GetSoundVolume(ALuint handle); - }; -} - -#endif // NOVELRT_AUDIO_AUDIOSERVICE_H diff --git a/audio/include/NovelRT/Audio/IAudioProvider.hpp b/audio/include/NovelRT/Audio/IAudioProvider.hpp new file mode 100644 index 000000000..b3bed3e54 --- /dev/null +++ b/audio/include/NovelRT/Audio/IAudioProvider.hpp @@ -0,0 +1,23 @@ +// Copyright © Matt Jones and Contributors. Licensed under the MIT Licence (MIT). See LICENCE.md in the repository root +// for more information. +#pragma once + +#include + +namespace NovelRT::Audio +{ + class IAudioProvider + { + protected: + virtual void Dispose() = 0; + + public: + virtual void OpenOutputStream() = 0; + virtual void CloseOutputStream() = 0; + virtual void SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer) = 0; + virtual void PlayOutputStream() = 0; + virtual void StopOutputStream() = 0; + virtual ~IAudioProvider() = 0; + + }; +} diff --git a/audio/include/NovelRT/Audio/OpenAL/OpenALAudioProvider.hpp b/audio/include/NovelRT/Audio/OpenAL/OpenALAudioProvider.hpp new file mode 100644 index 000000000..35cb18efd --- /dev/null +++ b/audio/include/NovelRT/Audio/OpenAL/OpenALAudioProvider.hpp @@ -0,0 +1,32 @@ +// Copyright © Matt Jones and Contributors. Licensed under the MIT Licence (MIT). See LICENCE.md in the repository root +// for more information. +#pragma once + +#include +#include +#include + +namespace NovelRT::Audio::OpenAL +{ + class OpenALAudioProvider : public IAudioProvider + { + private: + ALuint _outputSource; + ALCdevice* _device; + ALCcontext* _context; + + std::string GetALError(); + + protected: + void Dispose() final; + + public: + OpenALAudioProvider(); + + void OpenOutputStream() final; + void CloseOutputStream() final; + void PlayOutputStream() final; + void StopOutputStream() final; + void SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer) final; + }; +} diff --git a/include/NovelRT/ResourceManagement/AudioMetadata.h b/include/NovelRT/ResourceManagement/AudioMetadata.h index f126f9480..17355fdec 100644 --- a/include/NovelRT/ResourceManagement/AudioMetadata.h +++ b/include/NovelRT/ResourceManagement/AudioMetadata.h @@ -4,10 +4,6 @@ #ifndef NOVELRT_RESOURCEMANAGEMENT_AUDIOMETADATA_H #define NOVELRT_RESOURCEMANAGEMENT_AUDIOMETADATA_H -#ifndef NOVELRT_RESOURCEMANAGEMENT_H -#error NovelRT does not support including types explicitly by default. Please include ResourceManagement.h instead for the ResourceManagement namespace subset. -#endif - namespace NovelRT::ResourceManagement { struct AudioMetadata From a7057032b177d7ea3a459b1fdb03cd96a1de738f Mon Sep 17 00:00:00 2001 From: Ken Johnson Date: Fri, 16 Feb 2024 20:52:31 -0500 Subject: [PATCH 02/40] Fix spdlog to use external fmt Resolves an issue where spdlog's bundled fmt lib was using deprecated/non-standard MSFT extensions that Windows was removing soon --- thirdparty/CMakeLists.txt | 12 +++++++++--- thirdparty/fmt/CMakeLists.txt | 5 +++++ thirdparty/spdlog/CMakeLists.txt | 1 + 3 files changed, 15 insertions(+), 3 deletions(-) create mode 100644 thirdparty/fmt/CMakeLists.txt diff --git a/thirdparty/CMakeLists.txt b/thirdparty/CMakeLists.txt index 53d5c6ed8..71aa1921e 100644 --- a/thirdparty/CMakeLists.txt +++ b/thirdparty/CMakeLists.txt @@ -26,6 +26,10 @@ thirdparty_module(FLAC URL_HASH SHA256=8ff0607e75a322dd7cd6ec48f4f225471404ae2730d0ea945127b1355155e737 OVERRIDE_FIND_PACKAGE ) +thirdparty_module(fmt + URL https://github.com/fmtlib/fmt/releases/download/10.2.1/fmt-10.2.1.zip + URL_HASH SHA512=1cf0e3dd09c7d87e0890b8743559159d3be2a8f33c135516962d17c4eeb7b00659e6acd74518bd5566ee4e83ddaba155fecb4c229f90cd258b3b832e72ad82cd +) thirdparty_module(glfw3 URL https://github.com/glfw/glfw/archive/refs/tags/3.3.7.zip URL_HASH SHA512=0ee020ddbbed783b5f0d271ee0a98b37fe489b633e0a8407821f12b2bcfc3b80645751076192b5224bfe1d26d6092c27af9a9bcd7953a419dcec0182e3716341 @@ -71,8 +75,8 @@ thirdparty_module(SndFile URL_HASH SHA512=7e650af94068277246e4ccaf3b5dc20d0f93d2a2e0ecdf0f24f0be79196f879c21ec692ad48d39454f22dd01d9a4864d21458daa8d7b8f5ea4568c9551b345c1 ) thirdparty_module(spdlog - URL https://github.com/gabime/spdlog/archive/refs/tags/v1.10.0.zip - URL_HASH SHA512=9f1c778482446f52fb6e35f752226715412011f608bdcbfc87be5ae4a246d6733179a910fce09c2609e4dc1ba50664a6b0c3421749a7a12d8648dcf2b61c0b99 + URL https://github.com/gabime/spdlog/archive/refs/tags/v1.13.0.zip + URL_HASH SHA512=851febf19949006a17ead8b6392f12ae81e626926268829478d4d0d28a1dfe7682ef57a6c4f151031cf4cc60f1486cf8453999ee7ce40a1f0d3f545c4d7f8555 ) thirdparty_module(stduuid URL https://github.com/mariusbancila/stduuid/archive/3afe7193facd5d674de709fccc44d5055e144d7a.zip @@ -99,7 +103,9 @@ thirdparty_module(ZLIB # N.B. Order matters here, for dependency searching! foreach(module - nlohmann_json Fabulist + nlohmann_json + Fabulist + fmt glfw3 glm GSL diff --git a/thirdparty/fmt/CMakeLists.txt b/thirdparty/fmt/CMakeLists.txt new file mode 100644 index 000000000..7d8099c01 --- /dev/null +++ b/thirdparty/fmt/CMakeLists.txt @@ -0,0 +1,5 @@ +include(FetchContent) + +FetchContent_MakeAvailable(fmt) + +set_property(TARGET fmt PROPERTY POSITION_INDEPENDENT_CODE ON) diff --git a/thirdparty/spdlog/CMakeLists.txt b/thirdparty/spdlog/CMakeLists.txt index c9c3d1691..52676fddc 100644 --- a/thirdparty/spdlog/CMakeLists.txt +++ b/thirdparty/spdlog/CMakeLists.txt @@ -4,6 +4,7 @@ set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) set(SPDLOG_INSTALL ON) set(SPDLOG_ENABLE_PCH ON) +set(SPDLOG_FMT_EXTERNAL ON) FetchContent_MakeAvailable(spdlog) From bcc2261aba15d273ec0473abf6c9aa647524edc8 Mon Sep 17 00:00:00 2001 From: Ken Johnson Date: Sat, 17 Feb 2024 08:31:27 -0500 Subject: [PATCH 03/40] OpenAL partially working No audio playing due to bad handling of buffers --- audio/AudioMixer.cpp | 61 ++- audio/OpenAL/OpenALAudioProvider.cpp | 88 +++- audio/include/NovelRT/Audio/AudioMixer.hpp | 18 +- .../NovelRT/Audio/AudioSourceContext.hpp | 16 + .../NovelRT/Audio/AudioSourceState.hpp | 15 + .../include/NovelRT/Audio/IAudioProvider.hpp | 17 +- .../Audio/OpenAL/OpenALAudioProvider.hpp | 17 +- include/NovelRT/Ecs/Audio/AudioSystem.h | 10 +- include/NovelRT/Ecs/Audio/Ecs.Audio.h | 8 +- samples/AudioEcsSample/main.cpp | 4 +- src/NovelRT/Ecs/Audio/AudioSystem.cpp | 411 ++++++++---------- 11 files changed, 379 insertions(+), 286 deletions(-) create mode 100644 audio/include/NovelRT/Audio/AudioSourceContext.hpp create mode 100644 audio/include/NovelRT/Audio/AudioSourceState.hpp diff --git a/audio/AudioMixer.cpp b/audio/AudioMixer.cpp index 29b5d58eb..55020326a 100644 --- a/audio/AudioMixer.cpp +++ b/audio/AudioMixer.cpp @@ -9,28 +9,73 @@ namespace NovelRT::Audio { void AudioMixer::Initialise() { + _sourceContextCache = std::map(); _audioProvider = std::make_unique(); - _audioProvider->OpenOutputStream(); } - void AudioMixer::SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer) + uint32_t AudioMixer::SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span& buffer) { - _audioProvider->SubmitAudioBuffer(buffer); + auto newContext = AudioSourceContext(); + uint32_t sourceId = _audioProvider->SubmitAudioBuffer(buffer, newContext); + _sourceContextCache.emplace(sourceId, newContext); + return sourceId; } - void AudioMixer::PlayOutputStream() + AudioSourceState AudioMixer::GetSourceState(uint32_t id) { - _audioProvider->PlayOutputStream(); + return _audioProvider->GetSourceState(id); } - void AudioMixer::StopOutputStream() + void AudioMixer::PlaySource(uint32_t id) { - _audioProvider->StopOutputStream(); + _audioProvider->PlaySource(id); + } + + void AudioMixer::StopSource(uint32_t id) + { + _audioProvider->StopSource(id); + } + + void AudioMixer::PauseSource(uint32_t id) + { + _audioProvider->PauseSource(id); + } + + AudioSourceContext& AudioMixer::GetSourceContext(uint32_t id) + { + return _sourceContextCache.at(id); + } + + void AudioMixer::SetSourceContext(uint32_t id, AudioSourceContext& context) + { + _sourceContextCache.erase(id); + _sourceContextCache.emplace(id, context); + _audioProvider->SetSourceProperties(id, context); + } + + void AudioMixer::SetSourceVolume(uint32_t id, float volume) + { + auto& context = _sourceContextCache.at(id); + context.Volume = volume; + _audioProvider->SetSourceProperties(id, context); + } + + void AudioMixer::SetSourcePitch(uint32_t id, float pitch) + { + auto& context = _sourceContextCache.at(id); + context.Pitch = pitch; + _audioProvider->SetSourceProperties(id, context); + } + + void AudioMixer::SetSourceLoop(uint32_t id, bool isLooping) + { + auto& context = _sourceContextCache.at(id); + context.Loop = isLooping; + _audioProvider->SetSourceProperties(id, context); } void AudioMixer::TearDown() { - _audioProvider->CloseOutputStream(); _audioProvider.reset(); } diff --git a/audio/OpenAL/OpenALAudioProvider.cpp b/audio/OpenAL/OpenALAudioProvider.cpp index 2f2ed44aa..5bf3661e5 100644 --- a/audio/OpenAL/OpenALAudioProvider.cpp +++ b/audio/OpenAL/OpenALAudioProvider.cpp @@ -7,6 +7,9 @@ namespace NovelRT::Audio::OpenAL { OpenALAudioProvider::OpenALAudioProvider() { + _buffers = std::vector(); + _sources = std::vector(); + //Device and Context Init _device = alcOpenDevice(nullptr); if(!_device) @@ -17,37 +20,52 @@ namespace NovelRT::Audio::OpenAL } _context = alcCreateContext(_device, nullptr); alcMakeContextCurrent(_context); + } - OpenOutputStream(); + OpenALAudioProvider::~OpenALAudioProvider() + { + Dispose(); } void OpenALAudioProvider::Dispose() { - CloseOutputStream(); - - + alSourceStopv(static_cast(_sources.size()), _sources.data()); + alDeleteSources(static_cast(_sources.size()), _sources.data()); + _sources.clear(); + alDeleteBuffers(static_cast(_buffers.size()), _buffers.data()); + _buffers.clear(); + alcDestroyContext(_context); + alcCloseDevice(_device); } - void OpenALAudioProvider::OpenOutputStream() + uint32_t OpenALAudioProvider::OpenSource(AudioSourceContext& context) { - alGenSources(1, &_outputSource); - alSourcef(_outputSource, AL_GAIN, 0.75f); - alSourcef(_outputSource, AL_PITCH, 1.0f); + uint32_t source = 0; + alGenSources(1, &source); + alSourcef(source, AL_GAIN, context.Volume); + alSourcef(source, AL_PITCH, context.Pitch); + alSourcei(source, AL_LOOPING, static_cast(context.Loop)); + return source; } - void OpenALAudioProvider::CloseOutputStream() + // void OpenALAudioProvider::CloseSource() + // { + // alDeleteSources(1, &_outputSource); + // } + + void OpenALAudioProvider::PlaySource(uint32_t sourceId) { - alDeleteSources(1, &_outputSource); + alSourcePlay(sourceId); } - void OpenALAudioProvider::PlayOutputStream() + void OpenALAudioProvider::StopSource(uint32_t sourceId) { - + alSourceStop(sourceId); } - void OpenALAudioProvider::StopOutputStream() + void OpenALAudioProvider::PauseSource(uint32_t sourceId) { - + alSourcePause(sourceId); } std::string OpenALAudioProvider::GetALError() @@ -82,11 +100,49 @@ namespace NovelRT::Audio::OpenAL } } - void OpenALAudioProvider::SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer) + uint32_t OpenALAudioProvider::SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span& buffer, AudioSourceContext& context) { ALuint alBuffer; alGenBuffers(1, &alBuffer); alBufferData(alBuffer, AL_FORMAT_STEREO16, buffer.data(), static_cast(buffer.size() * sizeof(int16_t)), 44100); - alSourcei(_outputSource, AL_BUFFER, alBuffer); + uint32_t sourceId = OpenSource(context); + alSourcei(sourceId, AL_BUFFER, alBuffer); + return sourceId; + } + + void OpenALAudioProvider::SetSourceProperties(uint32_t sourceId, AudioSourceContext& context) + { + alSourcef(sourceId, AL_GAIN, context.Volume); + alSourcef(sourceId, AL_PITCH, context.Pitch); + alSourcei(sourceId, AL_LOOPING, static_cast(context.Loop)); } + + AudioSourceState OpenALAudioProvider::ConvertToAudioSourceState(ALenum oALSourceState) + { + switch(oALSourceState) + { + case AL_PLAYING: + { + return AudioSourceState::SOURCE_PLAYING; + } + case AL_PAUSED: + { + return AudioSourceState::SOURCE_PAUSED; + } + case AL_STOPPED: + case AL_INITIAL: + default: + { + return AudioSourceState::SOURCE_STOPPED; + } + } + } + + AudioSourceState OpenALAudioProvider::GetSourceState(uint32_t sourceId) + { + ALenum state = 0x0; + alGetSourcei(sourceId, AL_SOURCE_STATE, &state); + return ConvertToAudioSourceState(state); + } + } diff --git a/audio/include/NovelRT/Audio/AudioMixer.hpp b/audio/include/NovelRT/Audio/AudioMixer.hpp index a8f2f9a23..5f7a6c101 100644 --- a/audio/include/NovelRT/Audio/AudioMixer.hpp +++ b/audio/include/NovelRT/Audio/AudioMixer.hpp @@ -4,7 +4,10 @@ #include #include +#include #include +#include +#include #include namespace NovelRT::Audio @@ -13,13 +16,22 @@ namespace NovelRT::Audio { private: std::unique_ptr _audioProvider; + std::map _sourceContextCache; void TearDown(); public: void Initialise(); - void SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer); - void PlayOutputStream(); - void StopOutputStream(); + uint32_t SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span& buffer); + void PlaySource(uint32_t id); + void StopSource(uint32_t id); + void PauseSource(uint32_t id); + AudioSourceContext& GetSourceContext(uint32_t id); + void SetSourceContext(uint32_t id, AudioSourceContext& context); + void SetSourceVolume(uint32_t id, float volume); + void SetSourcePitch(uint32_t id, float pitch); + void SetSourceLoop(uint32_t id, bool isLooping); + AudioSourceState GetSourceState(uint32_t id); + ~AudioMixer(); }; } diff --git a/audio/include/NovelRT/Audio/AudioSourceContext.hpp b/audio/include/NovelRT/Audio/AudioSourceContext.hpp new file mode 100644 index 000000000..690399cd2 --- /dev/null +++ b/audio/include/NovelRT/Audio/AudioSourceContext.hpp @@ -0,0 +1,16 @@ +// Copyright © Matt Jones and Contributors. Licensed under the MIT Licence (MIT). See LICENCE.md in the repository root +// for more information. +#pragma once + +#include + +namespace NovelRT::Audio +{ + struct AudioSourceContext + { + public: + float Volume = 0.75f; + float Pitch = 1.0f; + bool Loop = false; + }; +} diff --git a/audio/include/NovelRT/Audio/AudioSourceState.hpp b/audio/include/NovelRT/Audio/AudioSourceState.hpp new file mode 100644 index 000000000..e683493a2 --- /dev/null +++ b/audio/include/NovelRT/Audio/AudioSourceState.hpp @@ -0,0 +1,15 @@ +// Copyright © Matt Jones and Contributors. Licensed under the MIT Licence (MIT). See LICENCE.md in the repository root +// for more information. +#pragma once + +#include + +namespace NovelRT::Audio +{ + enum class AudioSourceState: int32_t + { + SOURCE_STOPPED = 0, + SOURCE_PAUSED = 1, + SOURCE_PLAYING = 2 + }; +} diff --git a/audio/include/NovelRT/Audio/IAudioProvider.hpp b/audio/include/NovelRT/Audio/IAudioProvider.hpp index b3bed3e54..bcfdde785 100644 --- a/audio/include/NovelRT/Audio/IAudioProvider.hpp +++ b/audio/include/NovelRT/Audio/IAudioProvider.hpp @@ -3,6 +3,8 @@ #pragma once #include +#include +#include namespace NovelRT::Audio { @@ -10,14 +12,17 @@ namespace NovelRT::Audio { protected: virtual void Dispose() = 0; + virtual uint32_t OpenSource(AudioSourceContext& context) = 0; public: - virtual void OpenOutputStream() = 0; - virtual void CloseOutputStream() = 0; - virtual void SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer) = 0; - virtual void PlayOutputStream() = 0; - virtual void StopOutputStream() = 0; - virtual ~IAudioProvider() = 0; + virtual uint32_t SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span& buffer, AudioSourceContext& context) = 0; + virtual void PlaySource(uint32_t sourceId) = 0; + virtual void StopSource(uint32_t sourceId) = 0; + virtual void PauseSource(uint32_t sourceId) = 0; + virtual void SetSourceProperties(uint32_t sourceId, AudioSourceContext& context) = 0; + virtual AudioSourceState GetSourceState(uint32_t id) = 0; + + virtual ~IAudioProvider() = default; }; } diff --git a/audio/include/NovelRT/Audio/OpenAL/OpenALAudioProvider.hpp b/audio/include/NovelRT/Audio/OpenAL/OpenALAudioProvider.hpp index 35cb18efd..e73fb6d67 100644 --- a/audio/include/NovelRT/Audio/OpenAL/OpenALAudioProvider.hpp +++ b/audio/include/NovelRT/Audio/OpenAL/OpenALAudioProvider.hpp @@ -5,6 +5,7 @@ #include #include #include +#include namespace NovelRT::Audio::OpenAL { @@ -14,19 +15,25 @@ namespace NovelRT::Audio::OpenAL ALuint _outputSource; ALCdevice* _device; ALCcontext* _context; + std::vector _sources; + std::vector _buffers; std::string GetALError(); + AudioSourceState ConvertToAudioSourceState(ALenum oalSourceState); protected: void Dispose() final; + uint32_t OpenSource(AudioSourceContext& context) final; public: OpenALAudioProvider(); + void PlaySource(uint32_t sourceId) final; + void StopSource(uint32_t sourceId) final; + void PauseSource(uint32_t sourceId) final; + void SetSourceProperties(uint32_t sourceId, AudioSourceContext& context) final; + uint32_t SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span& buffer, AudioSourceContext& context) final; + AudioSourceState GetSourceState(uint32_t id) final; - void OpenOutputStream() final; - void CloseOutputStream() final; - void PlayOutputStream() final; - void StopOutputStream() final; - void SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer) final; + ~OpenALAudioProvider() final; }; } diff --git a/include/NovelRT/Ecs/Audio/AudioSystem.h b/include/NovelRT/Ecs/Audio/AudioSystem.h index 78ed4e5e0..2b84a5476 100644 --- a/include/NovelRT/Ecs/Audio/AudioSystem.h +++ b/include/NovelRT/Ecs/Audio/AudioSystem.h @@ -14,22 +14,20 @@ namespace NovelRT::Ecs::Audio { private: uint32_t _counter; - std::map> _fadeCache; LoggingService _logger; - std::map::iterator> _musicCache; - std::shared_ptr _service; - std::map _soundCache; + std::vector _soundCache; + std::shared_ptr _mixer; NovelRT::Timing::Timestamp _systemTime; std::shared_ptr _resourceManagerPluginProvider; - void ChangeAudioVolume(AudioEmitterComponent emitter, float desiredVolume); + // void ChangeAudioVolume(AudioEmitterComponent emitter, float desiredVolume); public: AudioSystem(std::shared_ptr resourceManagerPluginProvider); ~AudioSystem() noexcept; void Update(Timing::Timestamp delta, Ecs::Catalogue catalogue) final; - uint32_t CreateAudio(std::string fileName, bool isMusic); + uint32_t RegisterSound(std::string fileName); }; } diff --git a/include/NovelRT/Ecs/Audio/Ecs.Audio.h b/include/NovelRT/Ecs/Audio/Ecs.Audio.h index dad3e5cbc..96b621178 100644 --- a/include/NovelRT/Ecs/Audio/Ecs.Audio.h +++ b/include/NovelRT/Ecs/Audio/Ecs.Audio.h @@ -8,9 +8,11 @@ #error NovelRT does not support including types explicitly by default. Please include Ecs.h instead for the Ecs namespace subset. #endif -#include "../../Timing/StepTimer.h" -#include "../../Timing/Timestamp.h" -#include +#include +#include +#include +#include + #include #include diff --git a/samples/AudioEcsSample/main.cpp b/samples/AudioEcsSample/main.cpp index 37eecd0d3..3fd111341 100644 --- a/samples/AudioEcsSample/main.cpp +++ b/samples/AudioEcsSample/main.cpp @@ -41,12 +41,12 @@ int main() // Create the sound components std::string uwu = (soundDir / "uwu.ogg").string(); - auto uwuHandle = audioSystem->CreateAudio(uwu, true); + auto uwuHandle = audioSystem->RegisterSound(uwu); AudioEmitterComponent uwuComponent = AudioEmitterComponent{uwuHandle, true, -1, 0.75f}; AudioEmitterStateComponent uwuState = AudioEmitterStateComponent{AudioEmitterState::ToFadeIn, 8.0f, 0.75f}; std::string ahh = (soundDir / "goat.wav").string(); - auto goatHandle = audioSystem->CreateAudio(ahh, false); + auto goatHandle = audioSystem->RegisterSound(ahh); AudioEmitterComponent goatComponent = AudioEmitterComponent{goatHandle, false, 0, 0.75f}; AudioEmitterStateComponent goatState = AudioEmitterStateComponent{AudioEmitterState::Stopped, 0, 0}; diff --git a/src/NovelRT/Ecs/Audio/AudioSystem.cpp b/src/NovelRT/Ecs/Audio/AudioSystem.cpp index f2ac17b1a..2111e6f57 100644 --- a/src/NovelRT/Ecs/Audio/AudioSystem.cpp +++ b/src/NovelRT/Ecs/Audio/AudioSystem.cpp @@ -8,15 +8,13 @@ namespace NovelRT::Ecs::Audio AudioSystem::AudioSystem( std::shared_ptr resourceManagerPluginProvider) : _counter(1), - _fadeCache(std::map>()), _logger(Utilities::Misc::CONSOLE_LOG_AUDIO), - _musicCache(std::map::iterator>()), - _service(std::make_shared()), - _soundCache(std::map()), + _mixer(std::make_shared()), + _soundCache(std::vector()), _systemTime(Timing::Timestamp::zero()), _resourceManagerPluginProvider(std::move(resourceManagerPluginProvider)) { - _service->InitializeAudio(); + _mixer->Initialise(); } void AudioSystem::Update(Timing::Timestamp delta, Ecs::Catalogue catalogue) @@ -32,14 +30,7 @@ namespace NovelRT::Ecs::Audio { case AudioEmitterState::ToPlay: { - if (emitter.isMusic) - { - _service->PlayMusic(_musicCache.at(emitter.handle), emitter.numberOfLoops); - } - else - { - _service->PlaySound(_soundCache.at(emitter.handle), emitter.numberOfLoops); - } + _mixer->PlaySource(emitter.handle); _logger.logDebug("Entity ID {} - EmitterState ToPlay -> Playing", entity); states.PushComponentUpdateInstruction( entity, AudioEmitterStateComponent{AudioEmitterState::Playing, emitterState.fadeDuration}); @@ -47,14 +38,7 @@ namespace NovelRT::Ecs::Audio } case AudioEmitterState::ToStop: { - if (emitter.isMusic) - { - _service->StopMusic(); - } - else - { - _service->StopSound(_soundCache.at(emitter.handle)); - } + _mixer->StopSource(emitter.handle); _logger.logDebug("Entity ID {} - EmitterState ToStop -> Stopped", entity); states.PushComponentUpdateInstruction( entity, AudioEmitterStateComponent{AudioEmitterState::Stopped, emitterState.fadeDuration}); @@ -62,197 +46,172 @@ namespace NovelRT::Ecs::Audio } case AudioEmitterState::ToPause: { - if (emitter.isMusic) - { - _service->PauseMusic(); - _logger.logDebug("Entity ID {} - EmitterState ToPause -> Paused", entity); - states.PushComponentUpdateInstruction( - entity, AudioEmitterStateComponent{AudioEmitterState::Paused, emitterState.fadeDuration}); - } + _mixer->PauseSource(emitter.handle); + _logger.logDebug("Entity ID {} - EmitterState ToPause -> Paused", entity); + states.PushComponentUpdateInstruction( + entity, AudioEmitterStateComponent{AudioEmitterState::Paused, emitterState.fadeDuration}); break; } case AudioEmitterState::ToResume: { - if (emitter.isMusic) - { - _service->ResumeMusic(); - _logger.logDebug("Entity ID {} - EmitterState ToResume -> Playing", entity); - states.PushComponentUpdateInstruction( - entity, AudioEmitterStateComponent{AudioEmitterState::Playing, emitterState.fadeDuration}); - } + _mixer->PlaySource(emitter.handle); + _logger.logDebug("Entity ID {} - EmitterState ToResume -> Playing", entity); + states.PushComponentUpdateInstruction( + entity, AudioEmitterStateComponent{AudioEmitterState::Playing, emitterState.fadeDuration}); break; } - case AudioEmitterState::ToFadeOut: - { - if (emitter.isMusic && !_service->IsMusicPlaying()) - { - states.PushComponentUpdateInstruction( - entity, AudioEmitterStateComponent{AudioEmitterState::Stopped, 0.0f, 0.0f}); - break; - } - else if (!emitter.isMusic && !_service->IsSoundPlaying(_soundCache.at(emitter.handle))) - { - states.PushComponentUpdateInstruction( - entity, AudioEmitterStateComponent{AudioEmitterState::Stopped, 0.0f, 0.0f}); - break; - } + // case AudioEmitterState::ToFadeOut: + // { + // if (emitter.isMusic && !_mixer->IsMusicPlaying()) + // { + // states.PushComponentUpdateInstruction( + // entity, AudioEmitterStateComponent{AudioEmitterState::Stopped, 0.0f, 0.0f}); + // break; + // } + // else if (!emitter.isMusic && !_mixer->IsSoundPlaying(_soundCache.at(emitter.handle))) + // { + // states.PushComponentUpdateInstruction( + // entity, AudioEmitterStateComponent{AudioEmitterState::Stopped, 0.0f, 0.0f}); + // break; + // } - float slope = -(emitter.volume / emitterState.fadeDuration); - Timing::Timestamp endTime = _systemTime + Timing::Timestamp::fromSeconds(emitterState.fadeDuration); - _fadeCache.insert({entity, std::make_tuple(endTime, slope)}); + // float slope = -(emitter.volume / emitterState.fadeDuration); + // Timing::Timestamp endTime = _systemTime + Timing::Timestamp::fromSeconds(emitterState.fadeDuration); + // _fadeCache.insert({entity, std::make_tuple(endTime, slope)}); - states.PushComponentUpdateInstruction( - entity, - AudioEmitterStateComponent{AudioEmitterState::FadingOut, emitterState.fadeDuration, 0.0f}); - _logger.logDebug("Entity ID {} - EmitterState ToFadeOut -> FadingOut", entity); - break; - } - case AudioEmitterState::FadingOut: - { - Timing::Timestamp endTime = std::get<0>(_fadeCache.at(entity)); - float remainingDuration = (endTime - _systemTime).getSecondsFloat(); + // states.PushComponentUpdateInstruction( + // entity, + // AudioEmitterStateComponent{AudioEmitterState::FadingOut, emitterState.fadeDuration, 0.0f}); + // _logger.logDebug("Entity ID {} - EmitterState ToFadeOut -> FadingOut", entity); + // break; + // } + // case AudioEmitterState::FadingOut: + // { + // Timing::Timestamp endTime = std::get<0>(_fadeCache.at(entity)); + // float remainingDuration = (endTime - _systemTime).getSecondsFloat(); - if (_systemTime < endTime) - { - float slope = std::get<1>(_fadeCache.at(entity)); - float newVolume = emitter.volume + (slope * delta.getSecondsFloat()); - ChangeAudioVolume(emitter, newVolume); - states.PushComponentUpdateInstruction( - entity, AudioEmitterStateComponent{AudioEmitterState::FadingOut, remainingDuration, - emitterState.fadeExpectedVolume}); - emitters.PushComponentUpdateInstruction( - entity, - AudioEmitterComponent{emitter.handle, emitter.isMusic, emitter.numberOfLoops, newVolume}); - _logger.logDebug("Entity ID {} - EmitterState FadingOut at volume {}, slope: {}, currentTime: " - "{}, remaining: {}", - entity, newVolume, slope, _systemTime.getSecondsFloat(), remainingDuration); - break; - } - else - { - ChangeAudioVolume(emitter, 0.0f); - if (emitter.isMusic) - { - _service->StopMusic(); - } - else - { - _service->StopSound(_soundCache.at(emitter.handle)); - } + // if (_systemTime < endTime) + // { + // float slope = std::get<1>(_fadeCache.at(entity)); + // float newVolume = emitter.volume + (slope * delta.getSecondsFloat()); + // ChangeAudioVolume(emitter, newVolume); + // states.PushComponentUpdateInstruction( + // entity, AudioEmitterStateComponent{AudioEmitterState::FadingOut, remainingDuration, + // emitterState.fadeExpectedVolume}); + // emitters.PushComponentUpdateInstruction( + // entity, + // AudioEmitterComponent{emitter.handle, emitter.isMusic, emitter.numberOfLoops, newVolume}); + // _logger.logDebug("Entity ID {} - EmitterState FadingOut at volume {}, slope: {}, currentTime: " + // "{}, remaining: {}", + // entity, newVolume, slope, _systemTime.getSecondsFloat(), remainingDuration); + // break; + // } + // else + // { + // ChangeAudioVolume(emitter, 0.0f); + // if (emitter.isMusic) + // { + // _mixer->StopMusic(); + // } + // else + // { + // _mixer->StopSound(_soundCache.at(emitter.handle)); + // } - emitters.PushComponentUpdateInstruction( - entity, AudioEmitterComponent{emitter.handle, emitter.isMusic, emitter.numberOfLoops, - emitterState.fadeExpectedVolume}); - states.PushComponentUpdateInstruction( - entity, AudioEmitterStateComponent{AudioEmitterState::Stopped, 0.0f, 0.0f}); - _fadeCache.erase(entity); - _logger.logDebug("Entity ID {} - EmitterState FadingOut -> Stopped", entity); - break; - } + // emitters.PushComponentUpdateInstruction( + // entity, AudioEmitterComponent{emitter.handle, emitter.isMusic, emitter.numberOfLoops, + // emitterState.fadeExpectedVolume}); + // states.PushComponentUpdateInstruction( + // entity, AudioEmitterStateComponent{AudioEmitterState::Stopped, 0.0f, 0.0f}); + // _fadeCache.erase(entity); + // _logger.logDebug("Entity ID {} - EmitterState FadingOut -> Stopped", entity); + // break; + // } - break; - } - case AudioEmitterState::ToFadeIn: - { - if (emitter.isMusic && !_service->IsMusicPlaying()) - { - _service->SetMusicVolume(0.0f); - _service->PlayMusic(_musicCache.at(emitter.handle), emitter.numberOfLoops); - } - else if (!emitter.isMusic && !_service->IsSoundPlaying(emitter.handle)) - { - auto sound = _soundCache.at(emitter.handle); - _service->SetSoundVolume(sound, 0.0f); - _service->PlaySound(sound, emitter.numberOfLoops); - } - else - { - states.PushComponentUpdateInstruction( - entity, AudioEmitterStateComponent{AudioEmitterState::Playing, 0.0f, 0.0f}); - break; - } + // break; + // } + // case AudioEmitterState::ToFadeIn: + // { + // if (emitter.isMusic && !_mixer->IsMusicPlaying()) + // { + // _mixer->SetMusicVolume(0.0f); + // _mixer->PlayMusic(_musicCache.at(emitter.handle), emitter.numberOfLoops); + // } + // else if (!emitter.isMusic && !_mixer->IsSoundPlaying(emitter.handle)) + // { + // auto sound = _soundCache.at(emitter.handle); + // _mixer->SetSoundVolume(sound, 0.0f); + // _mixer->PlaySound(sound, emitter.numberOfLoops); + // } + // else + // { + // states.PushComponentUpdateInstruction( + // entity, AudioEmitterStateComponent{AudioEmitterState::Playing, 0.0f, 0.0f}); + // break; + // } - float slope = (emitterState.fadeExpectedVolume / emitterState.fadeDuration); - Timing::Timestamp endTime = _systemTime + Timing::Timestamp::fromSeconds(emitterState.fadeDuration); - _fadeCache.insert({entity, std::make_tuple(endTime, slope)}); + // float slope = (emitterState.fadeExpectedVolume / emitterState.fadeDuration); + // Timing::Timestamp endTime = _systemTime + Timing::Timestamp::fromSeconds(emitterState.fadeDuration); + // _fadeCache.insert({entity, std::make_tuple(endTime, slope)}); - states.PushComponentUpdateInstruction( - entity, AudioEmitterStateComponent{AudioEmitterState::FadingIn, emitterState.fadeDuration, - emitterState.fadeExpectedVolume}); - emitters.PushComponentUpdateInstruction( - entity, AudioEmitterComponent{emitter.handle, emitter.isMusic, emitter.numberOfLoops, 0.0f}); - _logger.logDebug("Entity ID {} - EmitterState ToFadeIn -> FadingIn", entity); - break; - } - case AudioEmitterState::FadingIn: - { - Timing::Timestamp endTime = std::get<0>(_fadeCache.at(entity)); - float remainingDuration = (endTime - _systemTime).getSecondsFloat(); + // states.PushComponentUpdateInstruction( + // entity, AudioEmitterStateComponent{AudioEmitterState::FadingIn, emitterState.fadeDuration, + // emitterState.fadeExpectedVolume}); + // emitters.PushComponentUpdateInstruction( + // entity, AudioEmitterComponent{emitter.handle, emitter.isMusic, emitter.numberOfLoops, 0.0f}); + // _logger.logDebug("Entity ID {} - EmitterState ToFadeIn -> FadingIn", entity); + // break; + // } + // case AudioEmitterState::FadingIn: + // { + // Timing::Timestamp endTime = std::get<0>(_fadeCache.at(entity)); + // float remainingDuration = (endTime - _systemTime).getSecondsFloat(); - if (_systemTime < endTime) - { - float slope = std::get<1>(_fadeCache.at(entity)); - float newVolume = emitter.volume + (slope * delta.getSecondsFloat()); - ChangeAudioVolume(emitter, newVolume); - states.PushComponentUpdateInstruction( - entity, AudioEmitterStateComponent{AudioEmitterState::FadingIn, remainingDuration, - emitterState.fadeExpectedVolume}); - emitters.PushComponentUpdateInstruction( - entity, - AudioEmitterComponent{emitter.handle, emitter.isMusic, emitter.numberOfLoops, newVolume}); - _logger.logDebug("Entity ID {} - EmitterState FadingIn at volume {}, slope: {}, currentTime: " - "{}, remaining: {}", - entity, newVolume, slope, _systemTime.getSecondsFloat(), remainingDuration); - break; - } - else - { - if (emitter.volume < emitterState.fadeExpectedVolume) - { - ChangeAudioVolume(emitter, emitterState.fadeExpectedVolume); - } - emitters.PushComponentUpdateInstruction( - entity, AudioEmitterComponent{emitter.handle, emitter.isMusic, emitter.numberOfLoops, - emitterState.fadeExpectedVolume}); - states.PushComponentUpdateInstruction( - entity, AudioEmitterStateComponent{AudioEmitterState::Playing, 0.0f, 0.0f}); - _fadeCache.erase(entity); - _logger.logDebug("Entity ID {} - EmitterState FadingIn -> Playing", entity); - break; - } - } + // if (_systemTime < endTime) + // { + // float slope = std::get<1>(_fadeCache.at(entity)); + // float newVolume = emitter.volume + (slope * delta.getSecondsFloat()); + // ChangeAudioVolume(emitter, newVolume); + // states.PushComponentUpdateInstruction( + // entity, AudioEmitterStateComponent{AudioEmitterState::FadingIn, remainingDuration, + // emitterState.fadeExpectedVolume}); + // emitters.PushComponentUpdateInstruction( + // entity, + // AudioEmitterComponent{emitter.handle, emitter.isMusic, emitter.numberOfLoops, newVolume}); + // _logger.logDebug("Entity ID {} - EmitterState FadingIn at volume {}, slope: {}, currentTime: " + // "{}, remaining: {}", + // entity, newVolume, slope, _systemTime.getSecondsFloat(), remainingDuration); + // break; + // } + // else + // { + // if (emitter.volume < emitterState.fadeExpectedVolume) + // { + // ChangeAudioVolume(emitter, emitterState.fadeExpectedVolume); + // } + // emitters.PushComponentUpdateInstruction( + // entity, AudioEmitterComponent{emitter.handle, emitter.isMusic, emitter.numberOfLoops, + // emitterState.fadeExpectedVolume}); + // states.PushComponentUpdateInstruction( + // entity, AudioEmitterStateComponent{AudioEmitterState::Playing, 0.0f, 0.0f}); + // _fadeCache.erase(entity); + // _logger.logDebug("Entity ID {} - EmitterState FadingIn -> Playing", entity); + // break; + // } + // } case AudioEmitterState::Playing: { - float currentVolume = 0; - if (emitter.isMusic) + auto soundContext = _mixer->GetSourceContext(emitter.handle); + if (soundContext.Volume != emitter.volume) { - currentVolume = _service->GetMusicVolume(); - if (currentVolume != emitter.volume) - { - _service->SetMusicVolume(emitter.volume); - _logger.logDebug("Entity ID {} - Emitter Volume {} -> {}", entity, currentVolume, - emitter.volume); - } - if (!_service->IsMusicPlaying()) - { - states.PushComponentUpdateInstruction( - entity, AudioEmitterStateComponent{AudioEmitterState::ToStop, 0.0f, 0.0f}); - } + _mixer->SetSourceVolume(emitter.handle, emitter.volume); + _logger.logDebug("Entity ID {} - Emitter Volume {} -> {}", entity, soundContext.Volume, + emitter.volume); } - else + if (_mixer->GetSourceState(emitter.handle) != NovelRT::Audio::AudioSourceState::SOURCE_PLAYING) { - currentVolume = _service->GetSoundVolume(_soundCache.at(emitter.handle)); - if (currentVolume != emitter.volume) - { - _service->SetSoundVolume(_soundCache.at(emitter.handle), emitter.volume); - _logger.logDebug("Entity ID {} - Emitter Volume {} -> {}", entity, currentVolume, - emitter.volume); - } - if (!_service->IsSoundPlaying(_soundCache.at(emitter.handle))) - { - states.PushComponentUpdateInstruction( - entity, AudioEmitterStateComponent{AudioEmitterState::ToStop, 0.0f, 0.0f}); - } + states.PushComponentUpdateInstruction( + entity, AudioEmitterStateComponent{AudioEmitterState::ToStop, 0.0f, 0.0f}); } break; } @@ -265,63 +224,41 @@ namespace NovelRT::Ecs::Audio } } - _service->CheckSources(); + //_mixer->CheckSources(); } - uint32_t AudioSystem::CreateAudio(std::string fileName, bool isMusic) + uint32_t AudioSystem::RegisterSound(std::string fileName) { if (_counter == UINT32_MAX) { - // add logging here return 0U; } - // 0 is not valid here since we're working with only positive numbers... not that anyone should be loading - // thousands of sounds. - uint32_t value = 0U; - if (isMusic) - { - auto asset = _resourceManagerPluginProvider->GetResourceLoader()->LoadAudioFrameData(fileName); - auto handle = _service->LoadMusic(asset.processedAudioFrames, asset.channelCount, asset.sampleRate); - if (_service->IsLoaded(handle)) - { - _musicCache.insert({_counter, handle}); - value = _counter; - _counter++; - } - } - else - { - auto asset = _resourceManagerPluginProvider->GetResourceLoader()->LoadAudioFrameData(fileName); - auto handle = _service->LoadSound(asset.processedAudioFrames, asset.channelCount, asset.sampleRate); - if (_service->IsLoaded(handle)) - { - _soundCache.insert({_counter, handle}); - value = _counter; - _counter++; - } - } - - return value; + auto asset = _resourceManagerPluginProvider->GetResourceLoader()->LoadAudioFrameData(fileName); + auto handle = _mixer->SubmitAudioBuffer(asset.processedAudioFrames); + // if (_mixer->IsLoaded(handle)) + // { + // _soundCache.insert({_counter, handle}); + // value = _counter; + _counter++; + // } + return handle; } - void AudioSystem::ChangeAudioVolume(AudioEmitterComponent emitter, float desiredVolume) - { - if (emitter.isMusic) - { - _service->SetMusicVolume(desiredVolume); - } - else - { - _service->SetSoundVolume(_soundCache.at(emitter.handle), desiredVolume); - } - } + // void AudioSystem::ChangeAudioVolume(AudioEmitterComponent emitter, float desiredVolume) + // { + // if (emitter.isMusic) + // { + // _mixer->SetMusicVolume(desiredVolume); + // } + // else + // { + // _mixer->SetSoundVolume(_soundCache.at(emitter.handle), desiredVolume); + // } + // } AudioSystem::~AudioSystem() noexcept { unused(_soundCache.empty()); - unused(_musicCache.empty()); - unused(_fadeCache.empty()); - _service->TearDown(); } } From 412c5bf0156b24fd6ab43bdec5fca844642e6357 Mon Sep 17 00:00:00 2001 From: Ken Johnson Date: Sat, 17 Feb 2024 22:04:49 -0500 Subject: [PATCH 04/40] OpenAL working again with proper shutdown and playback Kind of retrofitted to current ECS setup? but the point was to make it playback using OAL first and in a separate static lib. --- audio/AudioMixer.cpp | 6 +- audio/OpenAL/OpenALAudioProvider.cpp | 31 ++- audio/include/NovelRT/Audio/AudioMixer.hpp | 2 +- .../NovelRT/Audio/AudioSourceContext.hpp | 2 + .../include/NovelRT/Audio/IAudioProvider.hpp | 2 +- .../Audio/OpenAL/OpenALAudioProvider.hpp | 5 +- include/NovelRT/Ecs/Audio/AudioSystem.h | 3 +- src/NovelRT/Ecs/Audio/AudioSystem.cpp | 252 ++++++++---------- 8 files changed, 155 insertions(+), 148 deletions(-) diff --git a/audio/AudioMixer.cpp b/audio/AudioMixer.cpp index 55020326a..6c36a467a 100644 --- a/audio/AudioMixer.cpp +++ b/audio/AudioMixer.cpp @@ -13,9 +13,11 @@ namespace NovelRT::Audio _audioProvider = std::make_unique(); } - uint32_t AudioMixer::SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span& buffer) + uint32_t AudioMixer::SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, int32_t channelCount, int32_t originalSampleRate) { - auto newContext = AudioSourceContext(); + auto newContext = AudioSourceContext{}; + newContext.Channels = channelCount; + newContext.SampleRate = originalSampleRate; uint32_t sourceId = _audioProvider->SubmitAudioBuffer(buffer, newContext); _sourceContextCache.emplace(sourceId, newContext); return sourceId; diff --git a/audio/OpenAL/OpenALAudioProvider.cpp b/audio/OpenAL/OpenALAudioProvider.cpp index 5bf3661e5..48f9cf913 100644 --- a/audio/OpenAL/OpenALAudioProvider.cpp +++ b/audio/OpenAL/OpenALAudioProvider.cpp @@ -29,11 +29,12 @@ namespace NovelRT::Audio::OpenAL void OpenALAudioProvider::Dispose() { - alSourceStopv(static_cast(_sources.size()), _sources.data()); - alDeleteSources(static_cast(_sources.size()), _sources.data()); + alSourceStopv(static_cast(_sources.size()), reinterpret_cast(_sources.data())); + alDeleteSources(static_cast(_sources.size()), reinterpret_cast(_sources.data())); _sources.clear(); - alDeleteBuffers(static_cast(_buffers.size()), _buffers.data()); + alDeleteBuffers(static_cast(_buffers.size()), reinterpret_cast(_buffers.data())); _buffers.clear(); + alcMakeContextCurrent(NULL); alcDestroyContext(_context); alcCloseDevice(_device); } @@ -45,6 +46,7 @@ namespace NovelRT::Audio::OpenAL alSourcef(source, AL_GAIN, context.Volume); alSourcef(source, AL_PITCH, context.Pitch); alSourcei(source, AL_LOOPING, static_cast(context.Loop)); + _sources.emplace_back(static_cast(source)); return source; } @@ -68,7 +70,7 @@ namespace NovelRT::Audio::OpenAL alSourcePause(sourceId); } - std::string OpenALAudioProvider::GetALError() + void OpenALAudioProvider::GetALError() { auto err = alGetError(); switch (err) @@ -100,11 +102,12 @@ namespace NovelRT::Audio::OpenAL } } - uint32_t OpenALAudioProvider::SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span& buffer, AudioSourceContext& context) + uint32_t OpenALAudioProvider::SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, AudioSourceContext& context) { ALuint alBuffer; alGenBuffers(1, &alBuffer); - alBufferData(alBuffer, AL_FORMAT_STEREO16, buffer.data(), static_cast(buffer.size() * sizeof(int16_t)), 44100); + alBufferData(alBuffer, DetermineChannelFormat(context.Channels), buffer.data(), static_cast(buffer.size() * sizeof(int16_t)), context.SampleRate); + _buffers.emplace_back(static_cast(alBuffer)); uint32_t sourceId = OpenSource(context); alSourcei(sourceId, AL_BUFFER, alBuffer); return sourceId; @@ -145,4 +148,20 @@ namespace NovelRT::Audio::OpenAL return ConvertToAudioSourceState(state); } + ALenum OpenALAudioProvider::DetermineChannelFormat(int32_t numberOfChannels) + { + switch(numberOfChannels) + { + case 1: + return AL_FORMAT_MONO16; + case 5: + return AL_FORMAT_51CHN16; + case 7: + return AL_FORMAT_71CHN16; + case 2: + default: + return AL_FORMAT_STEREO16; + } + } + } diff --git a/audio/include/NovelRT/Audio/AudioMixer.hpp b/audio/include/NovelRT/Audio/AudioMixer.hpp index 5f7a6c101..fcc3b298d 100644 --- a/audio/include/NovelRT/Audio/AudioMixer.hpp +++ b/audio/include/NovelRT/Audio/AudioMixer.hpp @@ -21,7 +21,7 @@ namespace NovelRT::Audio public: void Initialise(); - uint32_t SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span& buffer); + uint32_t SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, int32_t channelCount, int32_t originalSampleRate); void PlaySource(uint32_t id); void StopSource(uint32_t id); void PauseSource(uint32_t id); diff --git a/audio/include/NovelRT/Audio/AudioSourceContext.hpp b/audio/include/NovelRT/Audio/AudioSourceContext.hpp index 690399cd2..4fd275a93 100644 --- a/audio/include/NovelRT/Audio/AudioSourceContext.hpp +++ b/audio/include/NovelRT/Audio/AudioSourceContext.hpp @@ -12,5 +12,7 @@ namespace NovelRT::Audio float Volume = 0.75f; float Pitch = 1.0f; bool Loop = false; + int32_t Channels = 2; + int32_t SampleRate = 44100; }; } diff --git a/audio/include/NovelRT/Audio/IAudioProvider.hpp b/audio/include/NovelRT/Audio/IAudioProvider.hpp index bcfdde785..75fd5072a 100644 --- a/audio/include/NovelRT/Audio/IAudioProvider.hpp +++ b/audio/include/NovelRT/Audio/IAudioProvider.hpp @@ -15,7 +15,7 @@ namespace NovelRT::Audio virtual uint32_t OpenSource(AudioSourceContext& context) = 0; public: - virtual uint32_t SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span& buffer, AudioSourceContext& context) = 0; + virtual uint32_t SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, AudioSourceContext& context) = 0; virtual void PlaySource(uint32_t sourceId) = 0; virtual void StopSource(uint32_t sourceId) = 0; virtual void PauseSource(uint32_t sourceId) = 0; diff --git a/audio/include/NovelRT/Audio/OpenAL/OpenALAudioProvider.hpp b/audio/include/NovelRT/Audio/OpenAL/OpenALAudioProvider.hpp index e73fb6d67..57ddd6363 100644 --- a/audio/include/NovelRT/Audio/OpenAL/OpenALAudioProvider.hpp +++ b/audio/include/NovelRT/Audio/OpenAL/OpenALAudioProvider.hpp @@ -18,8 +18,9 @@ namespace NovelRT::Audio::OpenAL std::vector _sources; std::vector _buffers; - std::string GetALError(); + void GetALError(); AudioSourceState ConvertToAudioSourceState(ALenum oalSourceState); + ALenum DetermineChannelFormat(int32_t numberOfChannels); protected: void Dispose() final; @@ -31,7 +32,7 @@ namespace NovelRT::Audio::OpenAL void StopSource(uint32_t sourceId) final; void PauseSource(uint32_t sourceId) final; void SetSourceProperties(uint32_t sourceId, AudioSourceContext& context) final; - uint32_t SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span& buffer, AudioSourceContext& context) final; + uint32_t SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, AudioSourceContext& context) final; AudioSourceState GetSourceState(uint32_t id) final; ~OpenALAudioProvider() final; diff --git a/include/NovelRT/Ecs/Audio/AudioSystem.h b/include/NovelRT/Ecs/Audio/AudioSystem.h index 2b84a5476..cb7ab9e43 100644 --- a/include/NovelRT/Ecs/Audio/AudioSystem.h +++ b/include/NovelRT/Ecs/Audio/AudioSystem.h @@ -15,7 +15,8 @@ namespace NovelRT::Ecs::Audio private: uint32_t _counter; LoggingService _logger; - std::vector _soundCache; + std::map _soundCache; + std::map> _fadeCache; std::shared_ptr _mixer; NovelRT::Timing::Timestamp _systemTime; std::shared_ptr _resourceManagerPluginProvider; diff --git a/src/NovelRT/Ecs/Audio/AudioSystem.cpp b/src/NovelRT/Ecs/Audio/AudioSystem.cpp index 2111e6f57..156489f13 100644 --- a/src/NovelRT/Ecs/Audio/AudioSystem.cpp +++ b/src/NovelRT/Ecs/Audio/AudioSystem.cpp @@ -10,7 +10,8 @@ namespace NovelRT::Ecs::Audio : _counter(1), _logger(Utilities::Misc::CONSOLE_LOG_AUDIO), _mixer(std::make_shared()), - _soundCache(std::vector()), + _soundCache(std::map()), + _fadeCache(std::map>()), _systemTime(Timing::Timestamp::zero()), _resourceManagerPluginProvider(std::move(resourceManagerPluginProvider)) { @@ -60,145 +61,127 @@ namespace NovelRT::Ecs::Audio entity, AudioEmitterStateComponent{AudioEmitterState::Playing, emitterState.fadeDuration}); break; } - // case AudioEmitterState::ToFadeOut: - // { - // if (emitter.isMusic && !_mixer->IsMusicPlaying()) - // { - // states.PushComponentUpdateInstruction( - // entity, AudioEmitterStateComponent{AudioEmitterState::Stopped, 0.0f, 0.0f}); - // break; - // } - // else if (!emitter.isMusic && !_mixer->IsSoundPlaying(_soundCache.at(emitter.handle))) - // { - // states.PushComponentUpdateInstruction( - // entity, AudioEmitterStateComponent{AudioEmitterState::Stopped, 0.0f, 0.0f}); - // break; - // } + case AudioEmitterState::ToFadeOut: + { + if (_mixer->GetSourceState(emitter.handle) == NovelRT::Audio::AudioSourceState::SOURCE_STOPPED) + { + states.PushComponentUpdateInstruction( + entity, AudioEmitterStateComponent{AudioEmitterState::Stopped, 0.0f, 0.0f}); + break; + } - // float slope = -(emitter.volume / emitterState.fadeDuration); - // Timing::Timestamp endTime = _systemTime + Timing::Timestamp::fromSeconds(emitterState.fadeDuration); - // _fadeCache.insert({entity, std::make_tuple(endTime, slope)}); + float slope = -(emitter.volume / emitterState.fadeDuration); + Timing::Timestamp endTime = _systemTime + Timing::Timestamp::fromSeconds(emitterState.fadeDuration); + _fadeCache.insert({entity, std::make_tuple(endTime, slope)}); - // states.PushComponentUpdateInstruction( - // entity, - // AudioEmitterStateComponent{AudioEmitterState::FadingOut, emitterState.fadeDuration, 0.0f}); - // _logger.logDebug("Entity ID {} - EmitterState ToFadeOut -> FadingOut", entity); - // break; - // } - // case AudioEmitterState::FadingOut: - // { - // Timing::Timestamp endTime = std::get<0>(_fadeCache.at(entity)); - // float remainingDuration = (endTime - _systemTime).getSecondsFloat(); + states.PushComponentUpdateInstruction( + entity, + AudioEmitterStateComponent{AudioEmitterState::FadingOut, emitterState.fadeDuration, 0.0f}); + _logger.logDebug("Entity ID {} - EmitterState ToFadeOut -> FadingOut", entity); + break; + } + case AudioEmitterState::FadingOut: + { + Timing::Timestamp endTime = std::get<0>(_fadeCache.at(entity)); + float remainingDuration = (endTime - _systemTime).getSecondsFloat(); - // if (_systemTime < endTime) - // { - // float slope = std::get<1>(_fadeCache.at(entity)); - // float newVolume = emitter.volume + (slope * delta.getSecondsFloat()); - // ChangeAudioVolume(emitter, newVolume); - // states.PushComponentUpdateInstruction( - // entity, AudioEmitterStateComponent{AudioEmitterState::FadingOut, remainingDuration, - // emitterState.fadeExpectedVolume}); - // emitters.PushComponentUpdateInstruction( - // entity, - // AudioEmitterComponent{emitter.handle, emitter.isMusic, emitter.numberOfLoops, newVolume}); - // _logger.logDebug("Entity ID {} - EmitterState FadingOut at volume {}, slope: {}, currentTime: " - // "{}, remaining: {}", - // entity, newVolume, slope, _systemTime.getSecondsFloat(), remainingDuration); - // break; - // } - // else - // { - // ChangeAudioVolume(emitter, 0.0f); - // if (emitter.isMusic) - // { - // _mixer->StopMusic(); - // } - // else - // { - // _mixer->StopSound(_soundCache.at(emitter.handle)); - // } + if (_systemTime < endTime) + { + float slope = std::get<1>(_fadeCache.at(entity)); + float newVolume = emitter.volume + (slope * delta.getSecondsFloat()); + _mixer->SetSourceVolume(emitter.handle, newVolume); - // emitters.PushComponentUpdateInstruction( - // entity, AudioEmitterComponent{emitter.handle, emitter.isMusic, emitter.numberOfLoops, - // emitterState.fadeExpectedVolume}); - // states.PushComponentUpdateInstruction( - // entity, AudioEmitterStateComponent{AudioEmitterState::Stopped, 0.0f, 0.0f}); - // _fadeCache.erase(entity); - // _logger.logDebug("Entity ID {} - EmitterState FadingOut -> Stopped", entity); - // break; - // } + states.PushComponentUpdateInstruction( + entity, AudioEmitterStateComponent{AudioEmitterState::FadingOut, remainingDuration, + emitterState.fadeExpectedVolume}); + emitters.PushComponentUpdateInstruction( + entity, + AudioEmitterComponent{emitter.handle, emitter.isMusic, emitter.numberOfLoops, newVolume}); + _logger.logDebug("Entity ID {} - EmitterState FadingOut at volume {}, slope: {}, currentTime: " + "{}, remaining: {}", + entity, newVolume, slope, _systemTime.getSecondsFloat(), remainingDuration); + break; + } + else + { + _mixer->SetSourceVolume(emitter.handle, 0.0f); + _mixer->StopSource(emitter.handle); - // break; - // } - // case AudioEmitterState::ToFadeIn: - // { - // if (emitter.isMusic && !_mixer->IsMusicPlaying()) - // { - // _mixer->SetMusicVolume(0.0f); - // _mixer->PlayMusic(_musicCache.at(emitter.handle), emitter.numberOfLoops); - // } - // else if (!emitter.isMusic && !_mixer->IsSoundPlaying(emitter.handle)) - // { - // auto sound = _soundCache.at(emitter.handle); - // _mixer->SetSoundVolume(sound, 0.0f); - // _mixer->PlaySound(sound, emitter.numberOfLoops); - // } - // else - // { - // states.PushComponentUpdateInstruction( - // entity, AudioEmitterStateComponent{AudioEmitterState::Playing, 0.0f, 0.0f}); - // break; - // } + emitters.PushComponentUpdateInstruction( + entity, AudioEmitterComponent{emitter.handle, emitter.isMusic, emitter.numberOfLoops, + emitterState.fadeExpectedVolume}); + states.PushComponentUpdateInstruction( + entity, AudioEmitterStateComponent{AudioEmitterState::Stopped, 0.0f, 0.0f}); + _fadeCache.erase(entity); + _logger.logDebug("Entity ID {} - EmitterState FadingOut -> Stopped", entity); + break; + } - // float slope = (emitterState.fadeExpectedVolume / emitterState.fadeDuration); - // Timing::Timestamp endTime = _systemTime + Timing::Timestamp::fromSeconds(emitterState.fadeDuration); - // _fadeCache.insert({entity, std::make_tuple(endTime, slope)}); + break; + } + case AudioEmitterState::ToFadeIn: + { + if (_mixer->GetSourceState(emitter.handle) == NovelRT::Audio::AudioSourceState::SOURCE_STOPPED) + { + _mixer->SetSourceVolume(emitter.handle, 0.0f); + _mixer->PlaySource(emitter.handle); + } + else + { + states.PushComponentUpdateInstruction( + entity, AudioEmitterStateComponent{AudioEmitterState::Playing, 0.0f, 0.0f}); + break; + } + + float slope = (emitterState.fadeExpectedVolume / emitterState.fadeDuration); + Timing::Timestamp endTime = _systemTime + Timing::Timestamp::fromSeconds(emitterState.fadeDuration); + _fadeCache.insert({entity, std::make_tuple(endTime, slope)}); - // states.PushComponentUpdateInstruction( - // entity, AudioEmitterStateComponent{AudioEmitterState::FadingIn, emitterState.fadeDuration, - // emitterState.fadeExpectedVolume}); - // emitters.PushComponentUpdateInstruction( - // entity, AudioEmitterComponent{emitter.handle, emitter.isMusic, emitter.numberOfLoops, 0.0f}); - // _logger.logDebug("Entity ID {} - EmitterState ToFadeIn -> FadingIn", entity); - // break; - // } - // case AudioEmitterState::FadingIn: - // { - // Timing::Timestamp endTime = std::get<0>(_fadeCache.at(entity)); - // float remainingDuration = (endTime - _systemTime).getSecondsFloat(); + states.PushComponentUpdateInstruction( + entity, AudioEmitterStateComponent{AudioEmitterState::FadingIn, emitterState.fadeDuration, + emitterState.fadeExpectedVolume}); + emitters.PushComponentUpdateInstruction( + entity, AudioEmitterComponent{emitter.handle, emitter.isMusic, emitter.numberOfLoops, 0.0f}); + _logger.logDebug("Entity ID {} - EmitterState ToFadeIn -> FadingIn", entity); + break; + } + case AudioEmitterState::FadingIn: + { + Timing::Timestamp endTime = std::get<0>(_fadeCache.at(entity)); + float remainingDuration = (endTime - _systemTime).getSecondsFloat(); - // if (_systemTime < endTime) - // { - // float slope = std::get<1>(_fadeCache.at(entity)); - // float newVolume = emitter.volume + (slope * delta.getSecondsFloat()); - // ChangeAudioVolume(emitter, newVolume); - // states.PushComponentUpdateInstruction( - // entity, AudioEmitterStateComponent{AudioEmitterState::FadingIn, remainingDuration, - // emitterState.fadeExpectedVolume}); - // emitters.PushComponentUpdateInstruction( - // entity, - // AudioEmitterComponent{emitter.handle, emitter.isMusic, emitter.numberOfLoops, newVolume}); - // _logger.logDebug("Entity ID {} - EmitterState FadingIn at volume {}, slope: {}, currentTime: " - // "{}, remaining: {}", - // entity, newVolume, slope, _systemTime.getSecondsFloat(), remainingDuration); - // break; - // } - // else - // { - // if (emitter.volume < emitterState.fadeExpectedVolume) - // { - // ChangeAudioVolume(emitter, emitterState.fadeExpectedVolume); - // } - // emitters.PushComponentUpdateInstruction( - // entity, AudioEmitterComponent{emitter.handle, emitter.isMusic, emitter.numberOfLoops, - // emitterState.fadeExpectedVolume}); - // states.PushComponentUpdateInstruction( - // entity, AudioEmitterStateComponent{AudioEmitterState::Playing, 0.0f, 0.0f}); - // _fadeCache.erase(entity); - // _logger.logDebug("Entity ID {} - EmitterState FadingIn -> Playing", entity); - // break; - // } - // } + if (_systemTime < endTime) + { + float slope = std::get<1>(_fadeCache.at(entity)); + float newVolume = emitter.volume + (slope * delta.getSecondsFloat()); + _mixer->SetSourceVolume(emitter.handle, newVolume); + states.PushComponentUpdateInstruction( + entity, AudioEmitterStateComponent{AudioEmitterState::FadingIn, remainingDuration, + emitterState.fadeExpectedVolume}); + emitters.PushComponentUpdateInstruction( + entity, + AudioEmitterComponent{emitter.handle, emitter.isMusic, emitter.numberOfLoops, newVolume}); + _logger.logDebug("Entity ID {} - EmitterState FadingIn at volume {}, slope: {}, currentTime: " + "{}, remaining: {}", + entity, newVolume, slope, _systemTime.getSecondsFloat(), remainingDuration); + break; + } + else + { + if (emitter.volume < emitterState.fadeExpectedVolume) + { + _mixer->SetSourceVolume(emitter.handle, emitterState.fadeExpectedVolume); + } + emitters.PushComponentUpdateInstruction( + entity, AudioEmitterComponent{emitter.handle, emitter.isMusic, emitter.numberOfLoops, + emitterState.fadeExpectedVolume}); + states.PushComponentUpdateInstruction( + entity, AudioEmitterStateComponent{AudioEmitterState::Playing, 0.0f, 0.0f}); + _fadeCache.erase(entity); + _logger.logDebug("Entity ID {} - EmitterState FadingIn -> Playing", entity); + break; + } + } case AudioEmitterState::Playing: { auto soundContext = _mixer->GetSourceContext(emitter.handle); @@ -223,8 +206,6 @@ namespace NovelRT::Ecs::Audio } } } - - //_mixer->CheckSources(); } uint32_t AudioSystem::RegisterSound(std::string fileName) @@ -235,7 +216,8 @@ namespace NovelRT::Ecs::Audio } auto asset = _resourceManagerPluginProvider->GetResourceLoader()->LoadAudioFrameData(fileName); - auto handle = _mixer->SubmitAudioBuffer(asset.processedAudioFrames); + auto handle = _mixer->SubmitAudioBuffer(NovelRT::Utilities::Misc::Span(asset.processedAudioFrames.data(), asset.processedAudioFrames.size()), asset.channelCount, asset.sampleRate); + _soundCache.emplace(handle, asset); // if (_mixer->IsLoaded(handle)) // { // _soundCache.insert({_counter, handle}); From cfe09c23cf6ecb25675d65b6b8de2e6f16c59896 Mon Sep 17 00:00:00 2001 From: Ken Johnson Date: Sun, 18 Feb 2024 00:17:58 -0500 Subject: [PATCH 05/40] Update OpenAL and hook logging --- CMakeLists.txt | 3 +- README.md | 7 +- audio/OpenAL/OpenALAudioProvider.cpp | 90 +++++++++++++++++-- .../Audio/OpenAL/OpenALAudioProvider.hpp | 6 +- thirdparty/CMakeLists.txt | 4 +- thirdparty/OpenAL/CMakeLists.txt | 2 +- 6 files changed, 98 insertions(+), 14 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 54e5b9fa2..63e8d0da7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -64,10 +64,11 @@ option(NOVELRT_BUILD_DEPS_WITH_MAX_CPU "Use all available CPU processing power w # set(NOVELRT_DOXYGEN_VERSION "1.8.17" CACHE STRING "Doxygen version") set(NOVELRT_FLAC_VERSION "1.3.4" CACHE STRING "FLAC version") +set(NOVELRT_FMT_VERSION "10.2.1" CACHE STRING "FMT version") set(NOVELRT_GLFW_VERSION "3.3.7" CACHE STRING "GLFW3 version") set(NOVELRT_GSL_VERSION "4.0.0" CACHE STRING "Microsoft.GSL version") set(NOVELRT_ONETBB_VERSION "2021.5.0" CACHE STRING "OneTBB version") -set(NOVELRT_OPENAL_VERSION "1.21.1" CACHE STRING "OpenAL version") +set(NOVELRT_OPENAL_VERSION "1.23.1" CACHE STRING "OpenAL version") set(NOVELRT_OGG_VERSION "1.3.5" CACHE STRING "Ogg version") set(NOVELRT_OPUS_VERSION "1.3.1" CACHE STRING "Opus version") set(NOVELRT_PNG_VERSION "1.6.35" CACHE STRING "PNG version") diff --git a/README.md b/README.md index 4acc66c9b..5e01a336e 100644 --- a/README.md +++ b/README.md @@ -41,12 +41,13 @@ The dependencies that are handled by CMake that do not need to be manually insta - GLFW 3.3.7 - glm 0.9.9.9 - gtest/gmock 1.11.0 +- fmt 10.2.1 - libpng 1.6.35 - libsndfile 1.1.0 - Microsoft GSL 4.0.0 - OneTBB 2021.5.0 -- OpenAL 1.21.1 -- spdlog 1.10.0 +- OpenAL 1.23.1 +- spdlog 1.13.0 ### Build instructions @@ -143,7 +144,7 @@ cmake .. -DCMAKE_APPLE_SILICON_PROCESSOR="arm64" If Vulkan SDK is not installed in a system path and the `setup-env.sh` file did not properly add the required environment variables, you can specify the `VULKAN_SDK` environment variable to your local Vulkan SDK location as such: ``` -VULKAN_SDK=/Users/youruser/Vulkan SDK/1.3.231.1/macOS cmake .. +VULKAN_SDK=/Users/youruser/Vulkan SDK/1.3.231.1/macOS cmake .. ``` Please ensure that the path includes the macOS folder, otherwise finding the proper libraries will fail. diff --git a/audio/OpenAL/OpenALAudioProvider.cpp b/audio/OpenAL/OpenALAudioProvider.cpp index 48f9cf913..be00e4c8b 100644 --- a/audio/OpenAL/OpenALAudioProvider.cpp +++ b/audio/OpenAL/OpenALAudioProvider.cpp @@ -2,13 +2,30 @@ // for more information. #include #include +#include namespace NovelRT::Audio::OpenAL { - OpenALAudioProvider::OpenALAudioProvider() + typedef void (ALC_APIENTRY*LoggingCallback)(void*, char, const char*, int) noexcept; + typedef void (ALC_APIENTRY*CallbackProvider)(LoggingCallback, void*) noexcept; + + OpenALAudioProvider::OpenALAudioProvider(): + _buffers(std::vector()), + _sources(std::vector()), + _logger(spdlog::stdout_color_mt("OpenAL")) { - _buffers = std::vector(); - _sources = std::vector(); + //Logger init + _logger->set_level(spdlog::level::debug); + CallbackProvider setLogCallback = reinterpret_cast(alcGetProcAddress(nullptr, "alsoft_set_log_callback")); + if (setLogCallback != nullptr) + { + setLogCallback(static_cast([](void* ptr, char l, const char* m, int) noexcept + { + auto provider = reinterpret_cast(ptr); + provider->LogOpenALMessages(l, m); + }), this); + } + //Device and Context Init _device = alcOpenDevice(nullptr); @@ -19,6 +36,12 @@ namespace NovelRT::Audio::OpenAL "OpenAL failed to create an audio device!", error); } _context = alcCreateContext(_device, nullptr); + if(!_context) + { + std::string error = GetALError(); + throw Exceptions::InitialisationFailureException( + "OpenAL failed to create and attach a proper context!", error); + } alcMakeContextCurrent(_context); } @@ -29,23 +52,35 @@ namespace NovelRT::Audio::OpenAL void OpenALAudioProvider::Dispose() { + alGetError(); alSourceStopv(static_cast(_sources.size()), reinterpret_cast(_sources.data())); + GetALError(); alDeleteSources(static_cast(_sources.size()), reinterpret_cast(_sources.data())); + GetALError(); _sources.clear(); alDeleteBuffers(static_cast(_buffers.size()), reinterpret_cast(_buffers.data())); + GetALError(); _buffers.clear(); alcMakeContextCurrent(NULL); + GetALError(); alcDestroyContext(_context); + GetALError(); alcCloseDevice(_device); + GetALError(); } uint32_t OpenALAudioProvider::OpenSource(AudioSourceContext& context) { uint32_t source = 0; + alGetError(); alGenSources(1, &source); + GetALError(); alSourcef(source, AL_GAIN, context.Volume); + GetALError(); alSourcef(source, AL_PITCH, context.Pitch); + GetALError(); alSourcei(source, AL_LOOPING, static_cast(context.Loop)); + GetALError(); _sources.emplace_back(static_cast(source)); return source; } @@ -57,47 +92,63 @@ namespace NovelRT::Audio::OpenAL void OpenALAudioProvider::PlaySource(uint32_t sourceId) { + alGetError(); alSourcePlay(sourceId); + GetALError(); } void OpenALAudioProvider::StopSource(uint32_t sourceId) { + alGetError(); alSourceStop(sourceId); + GetALError(); } void OpenALAudioProvider::PauseSource(uint32_t sourceId) { + alGetError(); alSourcePause(sourceId); + GetALError(); } - void OpenALAudioProvider::GetALError() + std::string OpenALAudioProvider::GetALError() { auto err = alGetError(); switch (err) { case AL_INVALID_NAME: { + _logger->error("A bad ID or name was passed to the OpenAL function."); return std::string("A bad ID or name was passed to the OpenAL function."); } case AL_INVALID_ENUM: { + _logger->error("An invalid enum was passed to an OpenAL function."); return std::string("An invalid enum was passed to an OpenAL function."); } case AL_INVALID_VALUE: { + _logger->error("An invalid value was passed to an OpenAL function."); return std::string("An invalid value was passed to an OpenAL function."); } case AL_INVALID_OPERATION: { + _logger->error("The requested operation is not valid."); return std::string("The requested operation is not valid."); } case AL_OUT_OF_MEMORY: { + _logger->error("The requested operation resulted in OpenAL running out of memory."); return std::string("The requested operation resulted in OpenAL running out of memory."); } + case AL_NO_ERROR: + { + return std::string(); + } default: { - return std::string(""); + _logger->error("Unknown OpenAL Error - Code: {err}", err); + return std::string("Unknown OpenAL Error - Code: " + err); } } } @@ -105,19 +156,27 @@ namespace NovelRT::Audio::OpenAL uint32_t OpenALAudioProvider::SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, AudioSourceContext& context) { ALuint alBuffer; + alGetError(); alGenBuffers(1, &alBuffer); + GetALError(); alBufferData(alBuffer, DetermineChannelFormat(context.Channels), buffer.data(), static_cast(buffer.size() * sizeof(int16_t)), context.SampleRate); + GetALError(); _buffers.emplace_back(static_cast(alBuffer)); uint32_t sourceId = OpenSource(context); alSourcei(sourceId, AL_BUFFER, alBuffer); + GetALError(); return sourceId; } void OpenALAudioProvider::SetSourceProperties(uint32_t sourceId, AudioSourceContext& context) { + alGetError(); alSourcef(sourceId, AL_GAIN, context.Volume); + GetALError(); alSourcef(sourceId, AL_PITCH, context.Pitch); + GetALError(); alSourcei(sourceId, AL_LOOPING, static_cast(context.Loop)); + GetALError(); } AudioSourceState OpenALAudioProvider::ConvertToAudioSourceState(ALenum oALSourceState) @@ -144,7 +203,9 @@ namespace NovelRT::Audio::OpenAL AudioSourceState OpenALAudioProvider::GetSourceState(uint32_t sourceId) { ALenum state = 0x0; + alGetError(); alGetSourcei(sourceId, AL_SOURCE_STATE, &state); + GetALError(); return ConvertToAudioSourceState(state); } @@ -164,4 +225,23 @@ namespace NovelRT::Audio::OpenAL } } + void OpenALAudioProvider::LogOpenALMessages(char level, const char* message) + { + switch(level) + { + case 'W': + { + _logger->warn(message); + } + case 'E': + { + _logger->error(message); + } + case 'I': + default: + { + _logger->debug(message); + } + } + } } diff --git a/audio/include/NovelRT/Audio/OpenAL/OpenALAudioProvider.hpp b/audio/include/NovelRT/Audio/OpenAL/OpenALAudioProvider.hpp index 57ddd6363..61f62720a 100644 --- a/audio/include/NovelRT/Audio/OpenAL/OpenALAudioProvider.hpp +++ b/audio/include/NovelRT/Audio/OpenAL/OpenALAudioProvider.hpp @@ -6,21 +6,23 @@ #include #include #include +#include namespace NovelRT::Audio::OpenAL { class OpenALAudioProvider : public IAudioProvider { private: - ALuint _outputSource; ALCdevice* _device; ALCcontext* _context; std::vector _sources; std::vector _buffers; + std::shared_ptr _logger; - void GetALError(); + std::string GetALError(); AudioSourceState ConvertToAudioSourceState(ALenum oalSourceState); ALenum DetermineChannelFormat(int32_t numberOfChannels); + void LogOpenALMessages(char level, const char* message); protected: void Dispose() final; diff --git a/thirdparty/CMakeLists.txt b/thirdparty/CMakeLists.txt index 71aa1921e..2dcd2fd05 100644 --- a/thirdparty/CMakeLists.txt +++ b/thirdparty/CMakeLists.txt @@ -56,8 +56,8 @@ thirdparty_module(Ogg OVERRIDE_FIND_PACKAGE ) thirdparty_module(OpenAL - URL https://github.com/kcat/openal-soft/archive/refs/tags/1.21.1.zip - URL_HASH SHA512=bc17d628548e59d0db3996917d207a4af0bbbf615ba3ba10ae8e99b28213e845e967a0510c8aad74e551aa66ddfb11499fe26082725af82160ac5d2db4e7e69d + URL https://github.com/kcat/openal-soft/archive/c03603b58d4cf6a25d36bca00305970bc9f163b4.zip + URL_HASH SHA512=331bf7d7ee138e3059dcdff68de153f73ba9b30dd098a0f5913d1e51d7292b8ad6f0e8642cb125bb8a757e70660a20375e5e76b70ddc59e5cbaab25676cbf9b8 OVERRIDE_FIND_PACKAGE ) thirdparty_module(Opus diff --git a/thirdparty/OpenAL/CMakeLists.txt b/thirdparty/OpenAL/CMakeLists.txt index 6edf185c6..a01bbabdf 100644 --- a/thirdparty/OpenAL/CMakeLists.txt +++ b/thirdparty/OpenAL/CMakeLists.txt @@ -27,7 +27,7 @@ target_compile_options(OpenAL $<$:-Wno-deprecated-copy> ) -target_compile_options(common +target_compile_options(alcommon PRIVATE $<$:/wd4127> $<$:/wd4834> From 14f9b2f5ba40172e3d77c93c721a90bdc49ddfaf Mon Sep 17 00:00:00 2001 From: Ken Johnson Date: Sun, 18 Feb 2024 09:17:51 -0500 Subject: [PATCH 06/40] Partially working XAudio2 for Windows Need to fix Ogg playback --- audio/AudioMixer.cpp | 9 +- audio/CMakeLists.txt | 11 +- audio/XAudio2/XAudio2AudioProvider.cpp | 176 ++++++++++++++++++ .../Audio/XAudio2/XAudio2AudioProvider.hpp | 45 +++++ 4 files changed, 237 insertions(+), 4 deletions(-) create mode 100644 audio/XAudio2/XAudio2AudioProvider.cpp create mode 100644 audio/include/NovelRT/Audio/XAudio2/XAudio2AudioProvider.hpp diff --git a/audio/AudioMixer.cpp b/audio/AudioMixer.cpp index 6c36a467a..762bcc0f9 100644 --- a/audio/AudioMixer.cpp +++ b/audio/AudioMixer.cpp @@ -3,14 +3,21 @@ #include //Conditional +#if defined(_WIN32) +#include +#else #include - +#endif namespace NovelRT::Audio { void AudioMixer::Initialise() { _sourceContextCache = std::map(); +#if defined(_WIN32) + _audioProvider = std::make_unique(); +#else _audioProvider = std::make_unique(); +#endif } uint32_t AudioMixer::SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, int32_t channelCount, int32_t originalSampleRate) diff --git a/audio/CMakeLists.txt b/audio/CMakeLists.txt index 1d873f634..ced6b14da 100644 --- a/audio/CMakeLists.txt +++ b/audio/CMakeLists.txt @@ -1,6 +1,8 @@ add_library(NovelRT-Audio STATIC AudioMixer.cpp - OpenAL/OpenALAudioProvider.cpp + $<$:XAudio2/XAudio2AudioProvider.cpp> + $<$:OpenAL/OpenALAudioProvider.cpp> + $<$:OpenAL/OpenALAudioProvider.cpp> ) target_sources(NovelRT-Audio @@ -11,7 +13,9 @@ target_sources(NovelRT-Audio FILES include/NovelRT/Audio/AudioMixer.hpp include/NovelRT/Audio/IAudioProvider.hpp - include/NovelRT/Audio/OpenAL/OpenALAudioProvider.hpp + $<$:include/NovelRT/Audio/XAudio2/XAudio2AudioProvider.hpp> + $<$:include/NovelRT/Audio/OpenAL/OpenALAudioProvider.hpp> + $<$:include/NovelRT/Audio/OpenAL/OpenALAudioProvider.hpp> ) set_target_properties(NovelRT-Audio @@ -49,7 +53,8 @@ target_include_directories(NovelRT-Audio PRIVATE "$:OpenAL> + $<$:OpenAL> ) install( diff --git a/audio/XAudio2/XAudio2AudioProvider.cpp b/audio/XAudio2/XAudio2AudioProvider.cpp new file mode 100644 index 000000000..91ceadc8e --- /dev/null +++ b/audio/XAudio2/XAudio2AudioProvider.cpp @@ -0,0 +1,176 @@ +// Copyright © Matt Jones and Contributors. Licensed under the MIT Licence (MIT). See LICENCE.md in the repository root +// for more information. +#include +#include +#include + +namespace NovelRT::Audio::XAudio2 +{ + + XAudio2AudioProvider::XAudio2AudioProvider(): + _device(nullptr), + _masterVoice(nullptr), + _sources(std::map()), + _sourceCounter(0), + _logger(spdlog::stdout_color_mt("XAudio2")), + _buffers(std::map()), + _bufferCounter(0) + { + //Logger init + _logger->set_level(spdlog::level::debug); + + //Device and Context Init + _hr = CoInitializeEx( nullptr, COINIT_MULTITHREADED ); + if (FAILED(_hr)) + { + throw new Exceptions::InitialisationFailureException("Failed to initialise COM!", _hr); + } + + if (FAILED(_hr = XAudio2Create( &_device, 0, XAUDIO2_DEFAULT_PROCESSOR))) + { + throw new Exceptions::InitialisationFailureException("Failed to create an instance of the XAudio2 engine!", _hr); + } + + if (FAILED(_hr = _device->CreateMasteringVoice(&_masterVoice, XAUDIO2_DEFAULT_CHANNELS, XAUDIO2_DEFAULT_SAMPLERATE))) + { + throw new Exceptions::InitialisationFailureException("Failed to create an output voice!", _hr); + } + + XAUDIO2_DEBUG_CONFIGURATION debug{}; + debug.TraceMask = XAUDIO2_LOG_ERRORS; + _device->SetDebugConfiguration(&debug); + + _logger->info("XAudio2 initialised."); + } + + XAudio2AudioProvider::~XAudio2AudioProvider() + { + Dispose(); + } + + void XAudio2AudioProvider::Dispose() + { + for(auto [id, source] : _sources) + { + source->FlushSourceBuffers(); + source->DestroyVoice(); + } + _sources.clear(); + _buffers.clear(); + _masterVoice->DestroyVoice(); + _device->StopEngine(); + } + + uint32_t XAudio2AudioProvider::OpenSource(AudioSourceContext& context) + { + uint32_t nextSource = ++_sourceCounter; + WAVEFORMATEX waveFormatContainer{}; + waveFormatContainer.wFormatTag = WAVE_FORMAT_PCM; + waveFormatContainer.nChannels = 2; + waveFormatContainer.nSamplesPerSec = static_cast(context.SampleRate); + waveFormatContainer.nAvgBytesPerSec = static_cast(context.SampleRate / 0.25); + waveFormatContainer.nBlockAlign = 4; + waveFormatContainer.wBitsPerSample = 16; + waveFormatContainer.cbSize = 0; + + + IXAudio2SourceVoice* newVoice; + if(FAILED(_hr = _device->CreateSourceVoice(&newVoice, &waveFormatContainer))) + { + _logger->error("Could not create source voice - Code: {hr}", _hr); + } + + _sources.emplace(nextSource, newVoice); + return nextSource; + } + + void XAudio2AudioProvider::PlaySource(uint32_t sourceId) + { + if(FAILED(_hr = _sources.at(sourceId)->Start(0))) + { + _logger->error("Error when attempting to play source {id} - Code: {hr}", sourceId, _hr); + } + } + + void XAudio2AudioProvider::StopSource(uint32_t sourceId) + { + if(FAILED(_hr = _sources.at(sourceId)->Stop(0))) + { + _logger->error("Error when stopping source {id} - Code: {hr}", sourceId, _hr); + } + } + + void XAudio2AudioProvider::PauseSource(uint32_t sourceId) + { + PlaySource(sourceId); + } + + uint32_t XAudio2AudioProvider::SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, AudioSourceContext& context) + { + uint32_t nextBuffer = ++_bufferCounter; + XAUDIO2_BUFFER xABuffer = + { + XAUDIO2_END_OF_STREAM, // Flags + static_cast(buffer.size()*sizeof(int16_t)), // AudioBytes + static_cast(new byte[buffer.size()*sizeof(int16_t)]) + }; + + std::memcpy((void*)(xABuffer.pAudioData), + reinterpret_cast(buffer.data()), buffer.size()*sizeof(int16_t)); + if(context.Loop) + { + xABuffer.LoopCount = XAUDIO2_LOOP_INFINITE; + } + + _buffers.emplace(nextBuffer, xABuffer); + uint32_t sourceId = OpenSource(context); + + if(FAILED(_hr = _sources.at(sourceId)->SubmitSourceBuffer(&xABuffer))) + { + _logger->error("Failed to submit buffer to source {sourceId} - Code: {hr}", sourceId, std::to_string(_hr)); + } + + return sourceId; + } + + void XAudio2AudioProvider::SetSourceProperties(uint32_t sourceId, AudioSourceContext& context) + { + //volume + if(FAILED(_hr = _sources.at(sourceId)->SetVolume(ConvertToXAudio2VolumeUnits(context.Volume)))) + { + _logger->error("Error when setting volume for source {id} - Code: {hr}", sourceId, _hr); + } + //pitch + if(FAILED(_hr = _sources.at(sourceId)->SetFrequencyRatio(context.Pitch))) + { + _logger->error("Error when setting pitch for source {id} - Code: {hr}", sourceId, _hr); + } + } + + AudioSourceState XAudio2AudioProvider::GetSourceState(uint32_t sourceId) + { + XAUDIO2_VOICE_STATE voiceState; + _sources.at(sourceId)->GetState(&voiceState, 0); + return ConvertToAudioSourceState(voiceState); + } + + float XAudio2AudioProvider::ConvertToXAudio2VolumeUnits(float inputVolume) + { + return inputVolume * (XAUDIO2_MAX_VOLUME_LEVEL - (-XAUDIO2_MAX_VOLUME_LEVEL)) + -XAUDIO2_MAX_VOLUME_LEVEL; + } + + AudioSourceState XAudio2AudioProvider::ConvertToAudioSourceState(XAUDIO2_VOICE_STATE sourceState) + { + switch(sourceState.BuffersQueued) + { + case NULL: + { + return AudioSourceState::SOURCE_STOPPED; + } + default: + { + return AudioSourceState::SOURCE_PLAYING; + } + } + } +} diff --git a/audio/include/NovelRT/Audio/XAudio2/XAudio2AudioProvider.hpp b/audio/include/NovelRT/Audio/XAudio2/XAudio2AudioProvider.hpp new file mode 100644 index 000000000..f17c94441 --- /dev/null +++ b/audio/include/NovelRT/Audio/XAudio2/XAudio2AudioProvider.hpp @@ -0,0 +1,45 @@ +// Copyright © Matt Jones and Contributors. Licensed under the MIT Licence (MIT). See LICENCE.md in the repository root +// for more information. +#pragma once + +#include +#include +#include +#include +#include + +namespace NovelRT::Audio::XAudio2 +{ + class XAudio2AudioProvider : public IAudioProvider + { + private: + IXAudio2* _device; + IXAudio2MasteringVoice* _masterVoice; + std::map _sources; + uint32_t _sourceCounter; + std::shared_ptr _logger; + uint32_t _bufferCounter; + std::map _buffers; + HRESULT _hr; + + float ConvertToXAudio2VolumeUnits(float inputVolume); + AudioSourceState ConvertToAudioSourceState(XAUDIO2_VOICE_STATE sourceState); + // ALenum DetermineChannelFormat(int32_t numberOfChannels); + // void LogOpenALMessages(char level, const char* message); + + protected: + void Dispose() final; + uint32_t OpenSource(AudioSourceContext& context) final; + + public: + XAudio2AudioProvider(); + void PlaySource(uint32_t sourceId) final; + void StopSource(uint32_t sourceId) final; + void PauseSource(uint32_t sourceId) final; + void SetSourceProperties(uint32_t sourceId, AudioSourceContext& context) final; + uint32_t SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, AudioSourceContext& context) final; + AudioSourceState GetSourceState(uint32_t id) final; + + ~XAudio2AudioProvider() final; + }; +} From 66719eae0c5e3dd6d782764c63c0d928fc69035f Mon Sep 17 00:00:00 2001 From: Ken Johnson Date: Sun, 18 Feb 2024 16:41:52 -0500 Subject: [PATCH 07/40] XAudio2 now conforming to AudioSystem --- audio/XAudio2/XAudio2AudioProvider.cpp | 49 ++++++++++----- .../Audio/XAudio2/XAudio2AudioProvider.hpp | 3 - src/NovelRT/CMakeLists.txt | 2 +- thirdparty/OpenAL/CMakeLists.txt | 61 ++++++++++--------- 4 files changed, 67 insertions(+), 48 deletions(-) diff --git a/audio/XAudio2/XAudio2AudioProvider.cpp b/audio/XAudio2/XAudio2AudioProvider.cpp index 91ceadc8e..33e78c6ba 100644 --- a/audio/XAudio2/XAudio2AudioProvider.cpp +++ b/audio/XAudio2/XAudio2AudioProvider.cpp @@ -36,6 +36,8 @@ namespace NovelRT::Audio::XAudio2 throw new Exceptions::InitialisationFailureException("Failed to create an output voice!", _hr); } + _masterVoice->SetVolume(1.0f); + XAUDIO2_DEBUG_CONFIGURATION debug{}; debug.TraceMask = XAUDIO2_LOG_ERRORS; _device->SetDebugConfiguration(&debug); @@ -79,6 +81,7 @@ namespace NovelRT::Audio::XAudio2 { _logger->error("Could not create source voice - Code: {hr}", _hr); } + newVoice->SetVolume(1.0f); _sources.emplace(nextSource, newVoice); return nextSource; @@ -102,19 +105,20 @@ namespace NovelRT::Audio::XAudio2 void XAudio2AudioProvider::PauseSource(uint32_t sourceId) { - PlaySource(sourceId); + StopSource(sourceId); } uint32_t XAudio2AudioProvider::SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, AudioSourceContext& context) { - uint32_t nextBuffer = ++_bufferCounter; + //uint32_t nextBuffer = ++_bufferCounter; XAUDIO2_BUFFER xABuffer = { XAUDIO2_END_OF_STREAM, // Flags static_cast(buffer.size()*sizeof(int16_t)), // AudioBytes - static_cast(new byte[buffer.size()*sizeof(int16_t)]) + static_cast(new byte[buffer.size()*sizeof(int16_t)]) //new buffer to copy int16_t* to }; + //Because XAudio2 expects a BYTE*, we'll have to cast it up and copy the data from the provided span :( std::memcpy((void*)(xABuffer.pAudioData), reinterpret_cast(buffer.data()), buffer.size()*sizeof(int16_t)); if(context.Loop) @@ -122,21 +126,20 @@ namespace NovelRT::Audio::XAudio2 xABuffer.LoopCount = XAUDIO2_LOOP_INFINITE; } - _buffers.emplace(nextBuffer, xABuffer); uint32_t sourceId = OpenSource(context); - if(FAILED(_hr = _sources.at(sourceId)->SubmitSourceBuffer(&xABuffer))) { _logger->error("Failed to submit buffer to source {sourceId} - Code: {hr}", sourceId, std::to_string(_hr)); } + _buffers.emplace(sourceId, xABuffer); return sourceId; } void XAudio2AudioProvider::SetSourceProperties(uint32_t sourceId, AudioSourceContext& context) { //volume - if(FAILED(_hr = _sources.at(sourceId)->SetVolume(ConvertToXAudio2VolumeUnits(context.Volume)))) + if(FAILED(_hr = _sources.at(sourceId)->SetVolume(context.Volume))) { _logger->error("Error when setting volume for source {id} - Code: {hr}", sourceId, _hr); } @@ -151,26 +154,42 @@ namespace NovelRT::Audio::XAudio2 { XAUDIO2_VOICE_STATE voiceState; _sources.at(sourceId)->GetState(&voiceState, 0); - return ConvertToAudioSourceState(voiceState); - } - - float XAudio2AudioProvider::ConvertToXAudio2VolumeUnits(float inputVolume) - { - return inputVolume * (XAUDIO2_MAX_VOLUME_LEVEL - (-XAUDIO2_MAX_VOLUME_LEVEL)) + -XAUDIO2_MAX_VOLUME_LEVEL; + AudioSourceState state = ConvertToAudioSourceState(voiceState); + if(state == AudioSourceState::SOURCE_STOPPED) + { + if(voiceState.pCurrentBufferContext == NULL && voiceState.BuffersQueued == 0 && voiceState.SamplesPlayed == 0) + { + auto source = _sources.at(sourceId); + source->Stop(0); + source->SubmitSourceBuffer(&_buffers.at(sourceId)); + } + } + return state; } AudioSourceState XAudio2AudioProvider::ConvertToAudioSourceState(XAUDIO2_VOICE_STATE sourceState) { - switch(sourceState.BuffersQueued) + if(sourceState.BuffersQueued == 0) { - case NULL: + if(sourceState.SamplesPlayed > 0 || (sourceState.pCurrentBufferContext == NULL && sourceState.SamplesPlayed == 0)) { return AudioSourceState::SOURCE_STOPPED; } - default: + else { return AudioSourceState::SOURCE_PLAYING; } } + else + { + if(sourceState.SamplesPlayed > 0) + { + return AudioSourceState::SOURCE_PLAYING; + } + else + { + return AudioSourceState::SOURCE_STOPPED; + } + } } } diff --git a/audio/include/NovelRT/Audio/XAudio2/XAudio2AudioProvider.hpp b/audio/include/NovelRT/Audio/XAudio2/XAudio2AudioProvider.hpp index f17c94441..0cf8c4c46 100644 --- a/audio/include/NovelRT/Audio/XAudio2/XAudio2AudioProvider.hpp +++ b/audio/include/NovelRT/Audio/XAudio2/XAudio2AudioProvider.hpp @@ -22,10 +22,7 @@ namespace NovelRT::Audio::XAudio2 std::map _buffers; HRESULT _hr; - float ConvertToXAudio2VolumeUnits(float inputVolume); AudioSourceState ConvertToAudioSourceState(XAUDIO2_VOICE_STATE sourceState); - // ALenum DetermineChannelFormat(int32_t numberOfChannels); - // void LogOpenALMessages(char level, const char* message); protected: void Dispose() final; diff --git a/src/NovelRT/CMakeLists.txt b/src/NovelRT/CMakeLists.txt index 5b1fd6d73..33cb8660d 100644 --- a/src/NovelRT/CMakeLists.txt +++ b/src/NovelRT/CMakeLists.txt @@ -71,7 +71,7 @@ target_link_libraries(Engine PUBLIC runtime tbb - OpenAL + $<$:OpenAL> sndfile glfw png diff --git a/thirdparty/OpenAL/CMakeLists.txt b/thirdparty/OpenAL/CMakeLists.txt index a01bbabdf..4e80a47fb 100644 --- a/thirdparty/OpenAL/CMakeLists.txt +++ b/thirdparty/OpenAL/CMakeLists.txt @@ -1,40 +1,43 @@ -include(FetchContent) +if(NOT ${WIN32}) + include(FetchContent) -set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) + set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) -set(ALSOFT_UTILS OFF) -set(ALSOFT_NO_CONFIG_UTIL ON) -set(ALSOFT_EXAMPLES OFF) -set(ALSOFT_INSTALL_EXAMPLES OFF) -set(ALSOFT_INSTALL_UTILS OFF) -set(ALSOFT_TESTS OFF) + set(ALSOFT_UTILS OFF) + set(ALSOFT_NO_CONFIG_UTIL ON) + set(ALSOFT_EXAMPLES OFF) + set(ALSOFT_INSTALL_EXAMPLES OFF) + set(ALSOFT_INSTALL_UTILS OFF) + set(ALSOFT_TESTS OFF) -set(BUILD_SHARED_LIBS ON) -set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS OFF) + set(BUILD_SHARED_LIBS ON) + set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS OFF) -FetchContent_MakeAvailable(OpenAL) -# These warnings are outside our scope as we do not manage the libraries themselves. -# We'll silence the warnings so that output is a little cleaner. -target_compile_options(OpenAL - PRIVATE - $<$:/wd4127> - $<$:/wd4834> + FetchContent_MakeAvailable(OpenAL) - $<$:-Wno-deprecated-copy> + # These warnings are outside our scope as we do not manage the libraries themselves. + # We'll silence the warnings so that output is a little cleaner. + target_compile_options(OpenAL + PRIVATE + $<$:/wd4127> + $<$:/wd4834> - $<$:-Wno-deprecated-copy> + $<$:-Wno-deprecated-copy> - $<$:-Wno-deprecated-copy> -) -target_compile_options(alcommon - PRIVATE - $<$:/wd4127> - $<$:/wd4834> + $<$:-Wno-deprecated-copy> - $<$:-Wno-deprecated-copy> + $<$:-Wno-deprecated-copy> + ) + target_compile_options(alcommon + PRIVATE + $<$:/wd4127> + $<$:/wd4834> - $<$:-Wno-deprecated-copy> + $<$:-Wno-deprecated-copy> - $<$:-Wno-deprecated-copy> -) + $<$:-Wno-deprecated-copy> + + $<$:-Wno-deprecated-copy> + ) +endif() From da30f470363c63dd52b107ddc69a66dad50f5e44 Mon Sep 17 00:00:00 2001 From: Ken Johnson Date: Wed, 21 Feb 2024 01:47:44 -0500 Subject: [PATCH 08/40] [UNTESTED] Obj-C++ wrapper for AVAudioEngine compiles --- CMakeLists.txt | 4 + .../AVAudioEngineAudioProvider.mm | 168 ++++++++++++++++++ audio/AudioMixer.cpp | 4 + audio/CMakeLists.txt | 30 +++- .../AVAudioEngineAudioProvider.hpp | 50 ++++++ samples/AudioEcsSample/CMakeLists.txt | 4 +- src/NovelRT/CMakeLists.txt | 9 +- thirdparty/OpenAL/CMakeLists.txt | 2 +- 8 files changed, 259 insertions(+), 12 deletions(-) create mode 100644 audio/AVAudioEngine/AVAudioEngineAudioProvider.mm create mode 100644 audio/include/NovelRT/Audio/AVAudioEngine/AVAudioEngineAudioProvider.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 63e8d0da7..c3371119a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,12 +34,16 @@ if(NOT DEFINED NOVELRT_TARGET) set(NOVELRT_TARGET "Win32" CACHE STRING "") elseif(APPLE) set(NOVELRT_TARGET "macOS" CACHE STRING "") + find_library(AVFOUNDATION_LIB AVFoundation) + find_library(FOUNDATION_LIB Foundation) + find_library(OBJC_LIB ObjC) elseif(UNIX) set(NOVELRT_TARGET "Linux" CACHE STRING "") else() set(NOVELRT_TARGET "Unknown" CACHE STRING "") endif() endif() +message("Using OS: ${NOVELRT_TARGET}") # # Prepend so that our FindVulkan gets picked up first when needed diff --git a/audio/AVAudioEngine/AVAudioEngineAudioProvider.mm b/audio/AVAudioEngine/AVAudioEngineAudioProvider.mm new file mode 100644 index 000000000..fae3149ea --- /dev/null +++ b/audio/AVAudioEngine/AVAudioEngineAudioProvider.mm @@ -0,0 +1,168 @@ +// Copyright © Matt Jones and Contributors. Licensed under the MIT Licence (MIT). See LICENCE.md in the repository root +// for more information. +#include +#include +#import +#import +#import +#import + + +namespace NovelRT::Audio::AVAudioEngine +{ + AVAudioEngineAudioProvider::AVAudioEngineAudioProvider(): + _impl(nullptr), + _buffers(std::map()), + _sources(std::map()), + _sourceEQUnits(std::map()), + _sourceStates(std::map()), + _logger(spdlog::stdout_color_mt("AVAudioEngine")) + { + //Logger init + _logger->set_level(spdlog::level::debug); + + //Device and Context Init + @try + { + ::NSError* err; + _impl = [[::AVAudioEngine alloc] init]; + [(::AVAudioEngine*)_impl prepare]; + if([(::AVAudioEngine*)_impl startAndReturnError: &err]) + { + _logger->error([err.localizedDescription UTF8String]); + return; + } + } + @catch(::NSException* ex) + { + std::string err = std::string([ex.reason UTF8String]); + _logger->error(err); + } + } + + AVAudioEngineAudioProvider::~AVAudioEngineAudioProvider() + { + Dispose(); + [(::AVAudioEngine*)_impl stop]; + [(::AVAudioEngine*)_impl release]; + } + + void AVAudioEngineAudioProvider::Dispose() + { + for(auto [id, source] : _sources) + { + [(::AVAudioPlayerNode*)source stop]; + [(::AVAudioPlayerNode*)source release]; + } + _sources.clear(); + + for(auto [id, source] : _sourceEQUnits) + { + [(::AVAudioUnitEQ*)source release]; + } + _sourceEQUnits.clear(); + + for(auto [id, buffer] : _buffers) + { + [(::AVAudioPCMBuffer*)buffer release]; + } + _buffers.clear(); + } + + uint32_t AVAudioEngineAudioProvider::OpenSource(AudioSourceContext& context) + { + uint32_t nextSource = ++_sourceCounter; + ::AVAudioPlayerNode* node = [[::AVAudioPlayerNode alloc] init]; + [(::AVAudioEngine*)_impl attachNode:node]; + _sources.emplace(nextSource, node); + _sourceStates.emplace(nextSource, AudioSourceState::SOURCE_STOPPED); + + ::AVAudioUnitEQ* eq = [[::AVAudioUnitEQ alloc] initWithNumberOfBands: 1]; + [(::AVAudioEngine*)_impl attachNode:eq]; + _sourceEQUnits.emplace(nextSource, eq); + + ::AVAudioFormat* format = [[::AVAudioFormat alloc] initWithCommonFormat:AVAudioCommonFormat::AVAudioPCMFormatInt16 sampleRate:context.SampleRate channels:context.Channels interleaved:false]; + ::AVAudioMixerNode* mixerNode = ((::AVAudioEngine*)_impl).mainMixerNode; + [(::AVAudioEngine*)_impl connect:node to:eq format:format]; + [(::AVAudioEngine*)_impl connect:eq to:mixerNode format:format]; + + return nextSource; + } + + void AVAudioEngineAudioProvider::PlaySource(uint32_t sourceId) + { + ::AVAudioPlayerNode* node = _sources.at(sourceId); + + if(!node.playing) + { + [(::AVAudioPlayerNode*)node play]; + } + + + _sourceStates[sourceId] = AudioSourceState::SOURCE_PLAYING; + + } + + void AVAudioEngineAudioProvider::StopSource(uint32_t sourceId) + { + ::AVAudioPlayerNode* node = _sources.at(sourceId); + if(node.playing) + { + [(::AVAudioPlayerNode*)node stop]; + } + _sourceStates[sourceId] = AudioSourceState::SOURCE_STOPPED; + } + + void AVAudioEngineAudioProvider::PauseSource(uint32_t sourceId) + { + ::AVAudioPlayerNode* node = _sources.at(sourceId); + if(node.playing) + { + [(::AVAudioPlayerNode*)node pause]; + } + _sourceStates[sourceId] = AudioSourceState::SOURCE_PAUSED; + } + + uint32_t AVAudioEngineAudioProvider::SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, AudioSourceContext& context) + { + ::AVAudioFormat* format = [[::AVAudioFormat alloc] initWithCommonFormat:AVAudioCommonFormat::AVAudioPCMFormatInt16 sampleRate:context.SampleRate channels:context.Channels interleaved:false]; + AudioBufferList abl; + abl.mNumberBuffers = 1; + abl.mBuffers[0].mData = (void *)buffer.data(); + abl.mBuffers[0].mNumberChannels = context.Channels; + abl.mBuffers[0].mDataByteSize = buffer.size() * sizeof(int16_t); + ::AVAudioPCMBuffer* pcmBuffer = [[::AVAudioPCMBuffer alloc] initWithPCMFormat:format bufferListNoCopy:&abl deallocator:NULL]; + + uint32_t sourceId = OpenSource(context); + + ::AVAudioPlayerNode* node = _sources.at(sourceId); + // AVAudioPlayerNodeBufferOptions options = AVAudioPlayerNodeBufferLoops; + + if(context.Loop) + { + [(::AVAudioPlayerNode*)node scheduleBuffer: pcmBuffer atTime: nil options: AVAudioPlayerNodeBufferLoops completionCallbackType: AVAudioPlayerNodeCompletionDataPlayedBack completionHandler: nil]; + } + else + { + [(::AVAudioPlayerNode*)node scheduleBuffer: pcmBuffer completionHandler: nil]; + } + _buffers.emplace(sourceId, pcmBuffer); + + return sourceId; + } + + void AVAudioEngineAudioProvider::SetSourceProperties(uint32_t sourceId, AudioSourceContext& context) + { + ::AVAudioUnitEQ* eq = _sourceEQUnits.at(sourceId); + eq.globalGain = context.Volume; + } + + AudioSourceState AVAudioEngineAudioProvider::GetSourceState(uint32_t sourceId) + { + return _sourceStates.at(sourceId); + } + + + + +} diff --git a/audio/AudioMixer.cpp b/audio/AudioMixer.cpp index 762bcc0f9..f99119bd2 100644 --- a/audio/AudioMixer.cpp +++ b/audio/AudioMixer.cpp @@ -5,6 +5,8 @@ //Conditional #if defined(_WIN32) #include +#elif __APPLE__ +#include #else #include #endif @@ -15,6 +17,8 @@ namespace NovelRT::Audio _sourceContextCache = std::map(); #if defined(_WIN32) _audioProvider = std::make_unique(); +#elif defined(__APPLE__) + _audioProvider = std::make_unique(); #else _audioProvider = std::make_unique(); #endif diff --git a/audio/CMakeLists.txt b/audio/CMakeLists.txt index ced6b14da..7710271bc 100644 --- a/audio/CMakeLists.txt +++ b/audio/CMakeLists.txt @@ -1,8 +1,8 @@ add_library(NovelRT-Audio STATIC AudioMixer.cpp - $<$:XAudio2/XAudio2AudioProvider.cpp> - $<$:OpenAL/OpenALAudioProvider.cpp> - $<$:OpenAL/OpenALAudioProvider.cpp> + $<$>:XAudio2/XAudio2AudioProvider.cpp> + $<$>:AVAudioEngine/AVAudioEngineAudioProvider.mm> + $<$>:OpenAL/OpenALAudioProvider.cpp> ) target_sources(NovelRT-Audio @@ -13,9 +13,17 @@ target_sources(NovelRT-Audio FILES include/NovelRT/Audio/AudioMixer.hpp include/NovelRT/Audio/IAudioProvider.hpp - $<$:include/NovelRT/Audio/XAudio2/XAudio2AudioProvider.hpp> - $<$:include/NovelRT/Audio/OpenAL/OpenALAudioProvider.hpp> - $<$:include/NovelRT/Audio/OpenAL/OpenALAudioProvider.hpp> +) + +target_sources(NovelRT-Audio + PRIVATE + FILE_SET private_headers + TYPE HEADERS + BASE_DIRS include + FILES + $<$>:include/NovelRT/Audio/XAudio2/XAudio2AudioProvider.hpp> + $<$>:include/NovelRT/Audio/OpenAL/OpenALAudioProvider.hpp> + $<$>:include/NovelRT/Audio/AVAudioEngine/AVAudioEngineAudioProvider.hpp> ) set_target_properties(NovelRT-Audio @@ -53,10 +61,16 @@ target_include_directories(NovelRT-Audio PRIVATE "$:OpenAL> - $<$:OpenAL> + $<$>:OpenAL> ) +if(${NOVELRT_TARGET} STREQUAL "macOS") + target_link_libraries(NovelRT-Audio + PRIVATE + "-framework AVFoundation" + ) +endif() + install( TARGETS NovelRT-Audio EXPORT NovelRTConfig diff --git a/audio/include/NovelRT/Audio/AVAudioEngine/AVAudioEngineAudioProvider.hpp b/audio/include/NovelRT/Audio/AVAudioEngine/AVAudioEngineAudioProvider.hpp new file mode 100644 index 000000000..13d5ec9b1 --- /dev/null +++ b/audio/include/NovelRT/Audio/AVAudioEngine/AVAudioEngineAudioProvider.hpp @@ -0,0 +1,50 @@ +// Copyright © Matt Jones and Contributors. Licensed under the MIT Licence (MIT). See LICENCE.md in the repository root +// for more information. +#pragma once + +#include +#include +#include +#include + +#ifdef __OBJC__ + @class AVAudioEngine; + @class AVAudioPlayerNode; + @class AVAudioPCMBuffer; + @class AVAudioUnitEQ; +#else + typedef struct objc_object AVAudioEngine; + typedef struct objc_object AVAudioPlayerNode; + typedef struct objc_object AVAudioPCMBuffer; + typedef struct objc_object AVAudioUnitEQ; +#endif + +namespace NovelRT::Audio::AVAudioEngine +{ + class AVAudioEngineAudioProvider : public IAudioProvider + { + private: + ::AVAudioEngine* _impl; // represents Obj-C object + std::map _sources; + std::map _sourceEQUnits; + std::map _buffers; + std::map _sourceStates; + std::shared_ptr _logger; + uint32_t _sourceCounter = 0; + + protected: + void Dispose() final; + uint32_t OpenSource(AudioSourceContext& context) final; + + public: + AVAudioEngineAudioProvider(); + void PlaySource(uint32_t sourceId) final; + void StopSource(uint32_t sourceId) final; + void PauseSource(uint32_t sourceId) final; + void SetSourceProperties(uint32_t sourceId, AudioSourceContext& context) final; + uint32_t SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, AudioSourceContext& context) final; + AudioSourceState GetSourceState(uint32_t id) final; + + ~AVAudioEngineAudioProvider() final; + }; +} diff --git a/samples/AudioEcsSample/CMakeLists.txt b/samples/AudioEcsSample/CMakeLists.txt index f6c1a9d52..3c3b78193 100644 --- a/samples/AudioEcsSample/CMakeLists.txt +++ b/samples/AudioEcsSample/CMakeLists.txt @@ -24,10 +24,10 @@ target_link_libraries(AudioEcsSample copy_build_products(AudioEcsSample DEPENDENCY Resources - TARGET_LOCATION $,$/../Resources,$/Resources> + TARGET_LOCATION $>,$/../Resources,$/Resources> ) -if(APPLE) +if(${NOVELRT_TARGET} STREQUAL "macOS") copy_runtime_dependencies(AudioEcsSample DEPENDENCY Engine LIBRARY Vulkan::MoltenVK diff --git a/src/NovelRT/CMakeLists.txt b/src/NovelRT/CMakeLists.txt index 33cb8660d..5fabb60bc 100644 --- a/src/NovelRT/CMakeLists.txt +++ b/src/NovelRT/CMakeLists.txt @@ -71,7 +71,6 @@ target_link_libraries(Engine PUBLIC runtime tbb - $<$:OpenAL> sndfile glfw png @@ -83,10 +82,18 @@ target_link_libraries(Engine FLAC opus ogg + $<$>:OpenAL> NovelRT-Graphics NovelRT-Audio ) +if(${NOVELRT_TARGET} STREQUAL "macOS") +target_link_libraries(Engine + PRIVATE + "-framework AVFoundation" +) +endif() + if(NOVELRT_INSTALL) install( TARGETS Engine diff --git a/thirdparty/OpenAL/CMakeLists.txt b/thirdparty/OpenAL/CMakeLists.txt index 4e80a47fb..fbf96edf2 100644 --- a/thirdparty/OpenAL/CMakeLists.txt +++ b/thirdparty/OpenAL/CMakeLists.txt @@ -1,4 +1,4 @@ -if(NOT ${WIN32}) +if(${NOVELRT_TARGET} STREQUAL Linux) include(FetchContent) set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) From f6bf98fa9a3578526faec35fef8c0097db6d4759 Mon Sep 17 00:00:00 2001 From: Ken Johnson Date: Wed, 21 Feb 2024 13:24:08 -0500 Subject: [PATCH 09/40] 50% success - usually runs with AVAudioEngine, sometimes crashes due to interruptions unhandled --- .../AVAudioEngineAudioProvider.mm | 132 ++++++++++++------ .../AVAudioEngineAudioProvider.hpp | 11 +- samples/AudioEcsSample/CMakeLists.txt | 4 + 3 files changed, 107 insertions(+), 40 deletions(-) diff --git a/audio/AVAudioEngine/AVAudioEngineAudioProvider.mm b/audio/AVAudioEngine/AVAudioEngineAudioProvider.mm index fae3149ea..f3cc59278 100644 --- a/audio/AVAudioEngine/AVAudioEngineAudioProvider.mm +++ b/audio/AVAudioEngine/AVAudioEngineAudioProvider.mm @@ -7,11 +7,9 @@ #import #import - namespace NovelRT::Audio::AVAudioEngine { AVAudioEngineAudioProvider::AVAudioEngineAudioProvider(): - _impl(nullptr), _buffers(std::map()), _sources(std::map()), _sourceEQUnits(std::map()), @@ -20,23 +18,40 @@ { //Logger init _logger->set_level(spdlog::level::debug); - + //Device and Context Init - @try + @try { ::NSError* err; - _impl = [[::AVAudioEngine alloc] init]; - [(::AVAudioEngine*)_impl prepare]; - if([(::AVAudioEngine*)_impl startAndReturnError: &err]) - { - _logger->error([err.localizedDescription UTF8String]); - return; - } + _logger->debug("calling alloc and init"); + _impl = [::AVAudioEngine new]; + + + _logger->debug("getting mainMixerNode and format"); + + _mixerFormat = [_impl.mainMixerNode outputFormatForBus:0]; + _logger->debug("Retrieved format - Channels {0}, SampleRate {1}", _mixerFormat.channelCount, _mixerFormat.sampleRate); + // ::AVAudioMixerNode* mixNode = [[::AVAudioMixerNode alloc] init]; + // _logger->debug("attaching mixer"); + // [(::AVAudioEngine*)_impl attachNode:mixNode]; + // _logger->debug("connecting nodes"); + // [(::AVAudioEngine*)_impl connect:mixNode to:_impl.outputNode format:_mixerFormat]; + //::AVAudioOutputNode* outputNode = ((::AVAudioEngine*)_impl).outputNode; + + + + // if([(::AVAudioEngine*)_impl startAndReturnError: &err]) + // { + // std::string error = std::string([err.localizedDescription UTF8String]); + // _logger->error(error); + // throw new Exceptions::InitialisationFailureException("Failed to initialise AVAudioEngine!", error); + // } } @catch(::NSException* ex) { std::string err = std::string([ex.reason UTF8String]); _logger->error(err); + throw new Exceptions::InitialisationFailureException("Failed to initialise AVAudioEngine!", err); } } @@ -71,34 +86,72 @@ uint32_t AVAudioEngineAudioProvider::OpenSource(AudioSourceContext& context) { + unused(context); + return _sourceCounter; + } + + uint32_t AVAudioEngineAudioProvider::OpenSourceInternal(AudioSourceContext& context, ::AVAudioPCMBuffer* buffer, ::AVAudioFormat* format) + { + if(format == nullptr) + { + _logger->error("SCREEEEEEEEEEEEEEEEEEEEEEEECH"); + } + uint32_t nextSource = ++_sourceCounter; - ::AVAudioPlayerNode* node = [[::AVAudioPlayerNode alloc] init]; - [(::AVAudioEngine*)_impl attachNode:node]; + ::AVAudioPlayerNode* node = [::AVAudioPlayerNode new]; + [_impl attachNode:node]; _sources.emplace(nextSource, node); _sourceStates.emplace(nextSource, AudioSourceState::SOURCE_STOPPED); ::AVAudioUnitEQ* eq = [[::AVAudioUnitEQ alloc] initWithNumberOfBands: 1]; - [(::AVAudioEngine*)_impl attachNode:eq]; + [_impl attachNode:eq]; _sourceEQUnits.emplace(nextSource, eq); - ::AVAudioFormat* format = [[::AVAudioFormat alloc] initWithCommonFormat:AVAudioCommonFormat::AVAudioPCMFormatInt16 sampleRate:context.SampleRate channels:context.Channels interleaved:false]; - ::AVAudioMixerNode* mixerNode = ((::AVAudioEngine*)_impl).mainMixerNode; - [(::AVAudioEngine*)_impl connect:node to:eq format:format]; - [(::AVAudioEngine*)_impl connect:eq to:mixerNode format:format]; - + _logger->debug("Connecting source {0} to EQ", nextSource); + [(::AVAudioEngine*)_impl connect:node to:eq format:nil]; + _logger->debug("Connecting source {0} EQ to mixer", nextSource); + [(::AVAudioEngine*)_impl connect:eq to:_impl.mainMixerNode format:nil]; + + //[eq play] + // if(context.Loop) + // { + // _logger->debug("Scheduling looping pcm buffer for source {0}", nextSource); + // [node scheduleBuffer: buffer atTime: nil options: AVAudioPlayerNodeBufferLoops completionCallbackType: AVAudioPlayerNodeCompletionDataPlayedBack completionHandler: nil]; + // } + // else + // { + // _logger->debug("Scheduling pcm buffer for source {0}", nextSource); + // [node scheduleBuffer: buffer completionHandler: nil]; + // } + _buffers.emplace(nextSource, buffer); + return nextSource; } void AVAudioEngineAudioProvider::PlaySource(uint32_t sourceId) { + if(!_impl.running) + { + _logger->debug("filling up gas tank..."); + [(::AVAudioEngine*)_impl prepare]; + ::NSError* err; + _logger->debug("starting engine.... VRROOOOOOOMMMMM...."); + [_impl startAndReturnError: &err]; + if(!_impl.running) + { + _logger->error("Could not start engine: {0}", std::string([err.localizedDescription UTF8String])); + } + } + ::AVAudioPlayerNode* node = _sources.at(sourceId); - + if(!node.playing) { - [(::AVAudioPlayerNode*)node play]; + [node scheduleBuffer: _buffers.at(sourceId) completionHandler: nil]; + [node play]; } - - + + _sourceStates[sourceId] = AudioSourceState::SOURCE_PLAYING; } @@ -125,30 +178,35 @@ uint32_t AVAudioEngineAudioProvider::SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, AudioSourceContext& context) { - ::AVAudioFormat* format = [[::AVAudioFormat alloc] initWithCommonFormat:AVAudioCommonFormat::AVAudioPCMFormatInt16 sampleRate:context.SampleRate channels:context.Channels interleaved:false]; + _logger->debug("Loading audio buffer - SampleRate: {0}, Channels: {1}", context.SampleRate, context.Channels); + ::AVAudioFormat* format = [[::AVAudioFormat alloc] initWithCommonFormat:AVAudioCommonFormat::AVAudioPCMFormatInt16 sampleRate:context.SampleRate channels:context.Channels interleaved:true]; AudioBufferList abl; abl.mNumberBuffers = 1; abl.mBuffers[0].mData = (void *)buffer.data(); abl.mBuffers[0].mNumberChannels = context.Channels; abl.mBuffers[0].mDataByteSize = buffer.size() * sizeof(int16_t); + ::AVAudioPCMBuffer* pcmBuffer = [[::AVAudioPCMBuffer alloc] initWithPCMFormat:format bufferListNoCopy:&abl deallocator:NULL]; - - uint32_t sourceId = OpenSource(context); - - ::AVAudioPlayerNode* node = _sources.at(sourceId); - // AVAudioPlayerNodeBufferOptions options = AVAudioPlayerNodeBufferLoops; + if(context.SampleRate != _mixerFormat.sampleRate) + { + ::AVAudioConverter* convert = [[::AVAudioConverter alloc] initFromFormat:format toFormat:_mixerFormat]; + + ::AVAudioPCMBuffer* newBuffer = [[::AVAudioPCMBuffer alloc] initWithPCMFormat:format frameCapacity:pcmBuffer.frameCapacity]; - if(context.Loop) + _logger->debug("converting..."); + [convert convertToBuffer:newBuffer error:nil withInputFromBlock: ^(::AVAudioPacketCount inNumberOfPackets, ::AVAudioConverterInputStatus *outStatus) { - [(::AVAudioPlayerNode*)node scheduleBuffer: pcmBuffer atTime: nil options: AVAudioPlayerNodeBufferLoops completionCallbackType: AVAudioPlayerNodeCompletionDataPlayedBack completionHandler: nil]; + *outStatus = AVAudioConverterInputStatus::AVAudioConverterInputStatus_HaveData; + return pcmBuffer; + }]; + // [convert convertToBuffer:newBuffer fromBuffer:pcmBuffer error:nil]; + + return OpenSourceInternal(context, newBuffer, format); } else { - [(::AVAudioPlayerNode*)node scheduleBuffer: pcmBuffer completionHandler: nil]; + return OpenSourceInternal(context, pcmBuffer, format); } - _buffers.emplace(sourceId, pcmBuffer); - - return sourceId; } void AVAudioEngineAudioProvider::SetSourceProperties(uint32_t sourceId, AudioSourceContext& context) @@ -161,8 +219,4 @@ { return _sourceStates.at(sourceId); } - - - - } diff --git a/audio/include/NovelRT/Audio/AVAudioEngine/AVAudioEngineAudioProvider.hpp b/audio/include/NovelRT/Audio/AVAudioEngine/AVAudioEngineAudioProvider.hpp index 13d5ec9b1..872700a05 100644 --- a/audio/include/NovelRT/Audio/AVAudioEngine/AVAudioEngineAudioProvider.hpp +++ b/audio/include/NovelRT/Audio/AVAudioEngine/AVAudioEngineAudioProvider.hpp @@ -7,24 +7,31 @@ #include #include + #ifdef __OBJC__ @class AVAudioEngine; @class AVAudioPlayerNode; @class AVAudioPCMBuffer; @class AVAudioUnitEQ; + @class AVAudioFormat; + ::AVAudioEngine* _impl; // represents Obj-C object + ::AVAudioFormat* _mixerFormat; #else typedef struct objc_object AVAudioEngine; typedef struct objc_object AVAudioPlayerNode; typedef struct objc_object AVAudioPCMBuffer; typedef struct objc_object AVAudioUnitEQ; + typedef struct objc_object AVAudioFormat; #endif + + namespace NovelRT::Audio::AVAudioEngine { class AVAudioEngineAudioProvider : public IAudioProvider { private: - ::AVAudioEngine* _impl; // represents Obj-C object + std::map _sources; std::map _sourceEQUnits; std::map _buffers; @@ -32,6 +39,8 @@ namespace NovelRT::Audio::AVAudioEngine std::shared_ptr _logger; uint32_t _sourceCounter = 0; + uint32_t OpenSourceInternal(AudioSourceContext& context, ::AVAudioPCMBuffer* buffer, ::AVAudioFormat* format); + protected: void Dispose() final; uint32_t OpenSource(AudioSourceContext& context) final; diff --git a/samples/AudioEcsSample/CMakeLists.txt b/samples/AudioEcsSample/CMakeLists.txt index 3c3b78193..c5123e152 100644 --- a/samples/AudioEcsSample/CMakeLists.txt +++ b/samples/AudioEcsSample/CMakeLists.txt @@ -28,6 +28,10 @@ copy_build_products(AudioEcsSample ) if(${NOVELRT_TARGET} STREQUAL "macOS") + target_link_libraries(AudioEcsSample + PRIVATE + "-framework AVFoundation" + ) copy_runtime_dependencies(AudioEcsSample DEPENDENCY Engine LIBRARY Vulkan::MoltenVK From 71a3726183baeae79b9a46a32c5d2e2fae907359 Mon Sep 17 00:00:00 2001 From: Ken Johnson Date: Sun, 10 Mar 2024 19:13:09 -0400 Subject: [PATCH 10/40] [WIP, WORKING] Switched to 32-bit float audio and static samplerate. Need to finish standardising samplerate conversion support and formats for XAudio and OpenAL --- .../AVAudioEngineAudioProvider.mm | 104 +++++++----------- audio/AudioMixer.cpp | 2 +- .../AVAudioEngineAudioProvider.hpp | 4 +- audio/include/NovelRT/Audio/AudioMixer.hpp | 2 +- .../include/NovelRT/Audio/IAudioProvider.hpp | 2 +- .../ResourceManagement/AudioMetadata.h | 2 +- src/NovelRT/CMakeLists.txt | 1 + src/NovelRT/Ecs/Audio/AudioSystem.cpp | 3 +- .../Desktop/DesktopResourceLoader.cpp | 36 +++++- thirdparty/CMakeLists.txt | 9 +- thirdparty/samplerate/CMakeLists.txt | 8 ++ 11 files changed, 98 insertions(+), 75 deletions(-) create mode 100644 thirdparty/samplerate/CMakeLists.txt diff --git a/audio/AVAudioEngine/AVAudioEngineAudioProvider.mm b/audio/AVAudioEngine/AVAudioEngineAudioProvider.mm index f3cc59278..b865810fc 100644 --- a/audio/AVAudioEngine/AVAudioEngineAudioProvider.mm +++ b/audio/AVAudioEngine/AVAudioEngineAudioProvider.mm @@ -14,6 +14,7 @@ _sources(std::map()), _sourceEQUnits(std::map()), _sourceStates(std::map()), + _sourceContexts(std::map()), _logger(spdlog::stdout_color_mt("AVAudioEngine")) { //Logger init @@ -25,27 +26,6 @@ ::NSError* err; _logger->debug("calling alloc and init"); _impl = [::AVAudioEngine new]; - - - _logger->debug("getting mainMixerNode and format"); - - _mixerFormat = [_impl.mainMixerNode outputFormatForBus:0]; - _logger->debug("Retrieved format - Channels {0}, SampleRate {1}", _mixerFormat.channelCount, _mixerFormat.sampleRate); - // ::AVAudioMixerNode* mixNode = [[::AVAudioMixerNode alloc] init]; - // _logger->debug("attaching mixer"); - // [(::AVAudioEngine*)_impl attachNode:mixNode]; - // _logger->debug("connecting nodes"); - // [(::AVAudioEngine*)_impl connect:mixNode to:_impl.outputNode format:_mixerFormat]; - //::AVAudioOutputNode* outputNode = ((::AVAudioEngine*)_impl).outputNode; - - - - // if([(::AVAudioEngine*)_impl startAndReturnError: &err]) - // { - // std::string error = std::string([err.localizedDescription UTF8String]); - // _logger->error(error); - // throw new Exceptions::InitialisationFailureException("Failed to initialise AVAudioEngine!", error); - // } } @catch(::NSException* ex) { @@ -82,6 +62,8 @@ [(::AVAudioPCMBuffer*)buffer release]; } _buffers.clear(); + + _sourceContexts.clear(); } uint32_t AVAudioEngineAudioProvider::OpenSource(AudioSourceContext& context) @@ -92,16 +74,12 @@ uint32_t AVAudioEngineAudioProvider::OpenSourceInternal(AudioSourceContext& context, ::AVAudioPCMBuffer* buffer, ::AVAudioFormat* format) { - if(format == nullptr) - { - _logger->error("SCREEEEEEEEEEEEEEEEEEEEEEEECH"); - } - uint32_t nextSource = ++_sourceCounter; ::AVAudioPlayerNode* node = [::AVAudioPlayerNode new]; [_impl attachNode:node]; _sources.emplace(nextSource, node); _sourceStates.emplace(nextSource, AudioSourceState::SOURCE_STOPPED); + _sourceContexts.emplace(nextSource, context); ::AVAudioUnitEQ* eq = [[::AVAudioUnitEQ alloc] initWithNumberOfBands: 1]; [_impl attachNode:eq]; @@ -111,18 +89,6 @@ [(::AVAudioEngine*)_impl connect:node to:eq format:nil]; _logger->debug("Connecting source {0} EQ to mixer", nextSource); [(::AVAudioEngine*)_impl connect:eq to:_impl.mainMixerNode format:nil]; - - //[eq play] - // if(context.Loop) - // { - // _logger->debug("Scheduling looping pcm buffer for source {0}", nextSource); - // [node scheduleBuffer: buffer atTime: nil options: AVAudioPlayerNodeBufferLoops completionCallbackType: AVAudioPlayerNodeCompletionDataPlayedBack completionHandler: nil]; - // } - // else - // { - // _logger->debug("Scheduling pcm buffer for source {0}", nextSource); - // [node scheduleBuffer: buffer completionHandler: nil]; - // } _buffers.emplace(nextSource, buffer); return nextSource; @@ -147,13 +113,32 @@ if(!node.playing) { - [node scheduleBuffer: _buffers.at(sourceId) completionHandler: nil]; + auto lambda = [this, sourceId = sourceId]() + { + this->UpdateSourceState(sourceId, AudioSourceState::SOURCE_STOPPED); + }; + if(_sourceContexts.at(sourceId).Loop) + { + _logger->debug("Looping source ID {0}", sourceId); + [node scheduleBuffer: _buffers.at(sourceId) atTime: nil options: AVAudioPlayerNodeBufferLoops completionHandler: nil]; + } + else + { + [node scheduleBuffer: _buffers.at(sourceId) completionHandler: lambda]; + } + + + + [node play]; } - _sourceStates[sourceId] = AudioSourceState::SOURCE_PLAYING; + } + void AVAudioEngineAudioProvider::UpdateSourceState(uint32_t sourceId, AudioSourceState state) + { + _sourceStates[sourceId] = state; } void AVAudioEngineAudioProvider::StopSource(uint32_t sourceId) @@ -176,41 +161,34 @@ _sourceStates[sourceId] = AudioSourceState::SOURCE_PAUSED; } - uint32_t AVAudioEngineAudioProvider::SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, AudioSourceContext& context) + uint32_t AVAudioEngineAudioProvider::SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, AudioSourceContext& context) { _logger->debug("Loading audio buffer - SampleRate: {0}, Channels: {1}", context.SampleRate, context.Channels); - ::AVAudioFormat* format = [[::AVAudioFormat alloc] initWithCommonFormat:AVAudioCommonFormat::AVAudioPCMFormatInt16 sampleRate:context.SampleRate channels:context.Channels interleaved:true]; + + uint32_t frameCap = buffer.size() * sizeof(float); + ::AVAudioFormat* format = [[::AVAudioFormat alloc] initWithCommonFormat:AVAudioCommonFormat::AVAudioPCMFormatFloat32 sampleRate:44100 channels:context.Channels interleaved:true]; + ::AVAudioFormat* deformat = [[::AVAudioFormat alloc] initWithCommonFormat:AVAudioCommonFormat::AVAudioPCMFormatFloat32 sampleRate:44100 channels:context.Channels interleaved:false]; AudioBufferList abl; abl.mNumberBuffers = 1; - abl.mBuffers[0].mData = (void *)buffer.data(); + abl.mBuffers[0].mData = ((void *)new Byte[frameCap]); abl.mBuffers[0].mNumberChannels = context.Channels; - abl.mBuffers[0].mDataByteSize = buffer.size() * sizeof(int16_t); - - ::AVAudioPCMBuffer* pcmBuffer = [[::AVAudioPCMBuffer alloc] initWithPCMFormat:format bufferListNoCopy:&abl deallocator:NULL]; - if(context.SampleRate != _mixerFormat.sampleRate) - { - ::AVAudioConverter* convert = [[::AVAudioConverter alloc] initFromFormat:format toFormat:_mixerFormat]; + abl.mBuffers[0].mDataByteSize = frameCap; - ::AVAudioPCMBuffer* newBuffer = [[::AVAudioPCMBuffer alloc] initWithPCMFormat:format frameCapacity:pcmBuffer.frameCapacity]; + std::memcpy((void*)abl.mBuffers[0].mData, reinterpret_cast(buffer.data()), frameCap); - _logger->debug("converting..."); - [convert convertToBuffer:newBuffer error:nil withInputFromBlock: ^(::AVAudioPacketCount inNumberOfPackets, ::AVAudioConverterInputStatus *outStatus) - { - *outStatus = AVAudioConverterInputStatus::AVAudioConverterInputStatus_HaveData; - return pcmBuffer; - }]; - // [convert convertToBuffer:newBuffer fromBuffer:pcmBuffer error:nil]; + ::AVAudioPCMBuffer* pcmBuffer = [[::AVAudioPCMBuffer alloc] initWithPCMFormat:format bufferListNoCopy:&abl deallocator:NULL]; - return OpenSourceInternal(context, newBuffer, format); - } - else - { - return OpenSourceInternal(context, pcmBuffer, format); - } + ::AVAudioConverter* convert = [[::AVAudioConverter alloc] initFromFormat:format toFormat:deformat]; + ::AVAudioPCMBuffer* deinterleavedBuffer = [[::AVAudioPCMBuffer alloc] initWithPCMFormat:deformat frameCapacity:pcmBuffer.frameCapacity]; + [convert convertToBuffer:deinterleavedBuffer fromBuffer:pcmBuffer error:nil]; + + return OpenSourceInternal(context, deinterleavedBuffer, format); } void AVAudioEngineAudioProvider::SetSourceProperties(uint32_t sourceId, AudioSourceContext& context) { + _sourceContexts[sourceId] = context; + ::AVAudioUnitEQ* eq = _sourceEQUnits.at(sourceId); eq.globalGain = context.Volume; } diff --git a/audio/AudioMixer.cpp b/audio/AudioMixer.cpp index f99119bd2..4c3a6d21c 100644 --- a/audio/AudioMixer.cpp +++ b/audio/AudioMixer.cpp @@ -24,7 +24,7 @@ namespace NovelRT::Audio #endif } - uint32_t AudioMixer::SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, int32_t channelCount, int32_t originalSampleRate) + uint32_t AudioMixer::SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, int32_t channelCount, int32_t originalSampleRate) { auto newContext = AudioSourceContext{}; newContext.Channels = channelCount; diff --git a/audio/include/NovelRT/Audio/AVAudioEngine/AVAudioEngineAudioProvider.hpp b/audio/include/NovelRT/Audio/AVAudioEngine/AVAudioEngineAudioProvider.hpp index 872700a05..034ff85eb 100644 --- a/audio/include/NovelRT/Audio/AVAudioEngine/AVAudioEngineAudioProvider.hpp +++ b/audio/include/NovelRT/Audio/AVAudioEngine/AVAudioEngineAudioProvider.hpp @@ -36,6 +36,7 @@ namespace NovelRT::Audio::AVAudioEngine std::map _sourceEQUnits; std::map _buffers; std::map _sourceStates; + std::map _sourceContexts; std::shared_ptr _logger; uint32_t _sourceCounter = 0; @@ -44,6 +45,7 @@ namespace NovelRT::Audio::AVAudioEngine protected: void Dispose() final; uint32_t OpenSource(AudioSourceContext& context) final; + void UpdateSourceState(uint32_t sourceId, AudioSourceState state); public: AVAudioEngineAudioProvider(); @@ -51,7 +53,7 @@ namespace NovelRT::Audio::AVAudioEngine void StopSource(uint32_t sourceId) final; void PauseSource(uint32_t sourceId) final; void SetSourceProperties(uint32_t sourceId, AudioSourceContext& context) final; - uint32_t SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, AudioSourceContext& context) final; + uint32_t SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, AudioSourceContext& context) final; AudioSourceState GetSourceState(uint32_t id) final; ~AVAudioEngineAudioProvider() final; diff --git a/audio/include/NovelRT/Audio/AudioMixer.hpp b/audio/include/NovelRT/Audio/AudioMixer.hpp index fcc3b298d..ed569a948 100644 --- a/audio/include/NovelRT/Audio/AudioMixer.hpp +++ b/audio/include/NovelRT/Audio/AudioMixer.hpp @@ -21,7 +21,7 @@ namespace NovelRT::Audio public: void Initialise(); - uint32_t SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, int32_t channelCount, int32_t originalSampleRate); + uint32_t SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, int32_t channelCount, int32_t originalSampleRate); void PlaySource(uint32_t id); void StopSource(uint32_t id); void PauseSource(uint32_t id); diff --git a/audio/include/NovelRT/Audio/IAudioProvider.hpp b/audio/include/NovelRT/Audio/IAudioProvider.hpp index 75fd5072a..99bb6b9f1 100644 --- a/audio/include/NovelRT/Audio/IAudioProvider.hpp +++ b/audio/include/NovelRT/Audio/IAudioProvider.hpp @@ -15,7 +15,7 @@ namespace NovelRT::Audio virtual uint32_t OpenSource(AudioSourceContext& context) = 0; public: - virtual uint32_t SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, AudioSourceContext& context) = 0; + virtual uint32_t SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, AudioSourceContext& context) = 0; virtual void PlaySource(uint32_t sourceId) = 0; virtual void StopSource(uint32_t sourceId) = 0; virtual void PauseSource(uint32_t sourceId) = 0; diff --git a/include/NovelRT/ResourceManagement/AudioMetadata.h b/include/NovelRT/ResourceManagement/AudioMetadata.h index 17355fdec..ef0663cbb 100644 --- a/include/NovelRT/ResourceManagement/AudioMetadata.h +++ b/include/NovelRT/ResourceManagement/AudioMetadata.h @@ -8,7 +8,7 @@ namespace NovelRT::ResourceManagement { struct AudioMetadata { - std::vector processedAudioFrames; + std::vector processedAudioFrames; int32_t channelCount; int32_t sampleRate; uuids::uuid databaseHandle; diff --git a/src/NovelRT/CMakeLists.txt b/src/NovelRT/CMakeLists.txt index 5fabb60bc..499bc5672 100644 --- a/src/NovelRT/CMakeLists.txt +++ b/src/NovelRT/CMakeLists.txt @@ -72,6 +72,7 @@ target_link_libraries(Engine runtime tbb sndfile + samplerate glfw png spdlog diff --git a/src/NovelRT/Ecs/Audio/AudioSystem.cpp b/src/NovelRT/Ecs/Audio/AudioSystem.cpp index 156489f13..38c7ce61c 100644 --- a/src/NovelRT/Ecs/Audio/AudioSystem.cpp +++ b/src/NovelRT/Ecs/Audio/AudioSystem.cpp @@ -31,6 +31,7 @@ namespace NovelRT::Ecs::Audio { case AudioEmitterState::ToPlay: { + _mixer->SetSourceLoop(emitter.handle, emitter.numberOfLoops > 0); _mixer->PlaySource(emitter.handle); _logger.logDebug("Entity ID {} - EmitterState ToPlay -> Playing", entity); states.PushComponentUpdateInstruction( @@ -216,7 +217,7 @@ namespace NovelRT::Ecs::Audio } auto asset = _resourceManagerPluginProvider->GetResourceLoader()->LoadAudioFrameData(fileName); - auto handle = _mixer->SubmitAudioBuffer(NovelRT::Utilities::Misc::Span(asset.processedAudioFrames.data(), asset.processedAudioFrames.size()), asset.channelCount, asset.sampleRate); + auto handle = _mixer->SubmitAudioBuffer(NovelRT::Utilities::Misc::Span(asset.processedAudioFrames.data(), asset.processedAudioFrames.size()), asset.channelCount, asset.sampleRate); _soundCache.emplace(handle, asset); // if (_mixer->IsLoaded(handle)) // { diff --git a/src/NovelRT/ResourceManagement/Desktop/DesktopResourceLoader.cpp b/src/NovelRT/ResourceManagement/Desktop/DesktopResourceLoader.cpp index 6bc2cab1c..119759b1f 100644 --- a/src/NovelRT/ResourceManagement/Desktop/DesktopResourceLoader.cpp +++ b/src/NovelRT/ResourceManagement/Desktop/DesktopResourceLoader.cpp @@ -3,6 +3,7 @@ #include #include +#include namespace NovelRT::ResourceManagement::Desktop { @@ -389,6 +390,7 @@ namespace NovelRT::ResourceManagement::Desktop AudioMetadata DesktopResourceLoader::LoadAudioFrameData(std::filesystem::path filePath) { constexpr size_t _bufferSize = 2048; + constexpr int32_t _sampleRate = 44100; if (filePath.is_relative()) { @@ -405,24 +407,48 @@ namespace NovelRT::ResourceManagement::Desktop throw NovelRT::Exceptions::IOException(filePath.string(), std::string(sf_strerror(file))); } - std::vector data; - std::vector readBuffer; + std::vector data; + std::vector readBuffer; readBuffer.resize(_bufferSize); - sf_command(file, SFC_SET_SCALE_FLOAT_INT_READ, nullptr, SF_TRUE); + //sf_command(file, SFC_SET_SCALE, nullptr, SF_TRUE); sf_count_t readSize = 0; - while ((readSize = sf_read_short(file, readBuffer.data(), static_cast(readBuffer.size()))) != 0) + while ((readSize = sf_read_float(file, readBuffer.data(), static_cast(readBuffer.size()))) != 0) { data.insert(data.end(), readBuffer.begin(), readBuffer.begin() + readSize); } sf_close(file); - + auto relativePathForAssetDatabase = std::filesystem::relative(filePath, _resourcesRootDirectory); uuids::uuid databaseHandle = RegisterAsset(relativePathForAssetDatabase); + if(info.samplerate != _sampleRate) + { + _logger.logDebug("Detected sample rate of {0}", info.samplerate); + info.samplerate > 44100 ? _logger.logDebug("Downscaling...") : _logger.logDebug("Upscaling..."); + std::vector resampledData = std::vector(data.size()); + SRC_DATA conversionInfo = SRC_DATA{}; + conversionInfo.data_in = data.data(); + conversionInfo.data_out = resampledData.data(); + conversionInfo.input_frames = info.channels == 1 ? data.size() : data.size() / info.channels; + conversionInfo.output_frames = conversionInfo.input_frames; + double rate = 44100.0 / static_cast(info.samplerate); + _logger.logDebug("Scaling by ratio of {0:f}", rate); + conversionInfo.src_ratio = rate; + int result = src_simple(&conversionInfo, SRC_SINC_MEDIUM_QUALITY, info.channels); + if(result != 0) + { + std::string err = src_strerror(result); + _logger.logErrorLine(err); + throw new NovelRT::Exceptions::InvalidOperationException(err); + } + + return AudioMetadata{resampledData, info.channels, _sampleRate, databaseHandle}; + } + return AudioMetadata{data, info.channels, info.samplerate, databaseHandle}; } diff --git a/thirdparty/CMakeLists.txt b/thirdparty/CMakeLists.txt index 2dcd2fd05..3fb3d1e1a 100644 --- a/thirdparty/CMakeLists.txt +++ b/thirdparty/CMakeLists.txt @@ -70,6 +70,12 @@ thirdparty_module(PNG URL_HASH SHA512=65df0c2befa06d0b9ec0c6395a73987fb071754610d328b94ce01a6b8f7161c71ce97ddf0e0a8e28c2e50019a103368c4b4f0b63bd8cd75e168b32f940922d96 OVERRIDE_FIND_PACKAGE ) + +thirdparty_module(samplerate + URL https://github.com/libsndfile/libsamplerate/archive/refs/tags/0.2.2.zip + URL_HASH SHA512=dedd072bc83ccebfe1a33e8a3fb3f72559bfd36282b958b1d5463b786f2325f684137580db38d91f0f4f5345a1da3e9e1c4a1695e303182e57d542c085db829f +) + thirdparty_module(SndFile URL https://github.com/novelrt/libsndfile/archive/f76d63813c810c2ed09bdb3821bb807498a7558e.zip URL_HASH SHA512=7e650af94068277246e4ccaf3b5dc20d0f93d2a2e0ecdf0f24f0be79196f879c21ec692ad48d39454f22dd01d9a4864d21458daa8d7b8f5ea4568c9551b345c1 @@ -110,7 +116,8 @@ foreach(module glm GSL GTest - Opus Ogg FLAC Vorbis SndFile OpenAL + Opus Ogg FLAC Vorbis SndFile samplerate + OpenAL ZLIB PNG spdlog stduuid diff --git a/thirdparty/samplerate/CMakeLists.txt b/thirdparty/samplerate/CMakeLists.txt new file mode 100644 index 000000000..203ae7f5d --- /dev/null +++ b/thirdparty/samplerate/CMakeLists.txt @@ -0,0 +1,8 @@ +include(FetchContent) + +set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) + +set(BUILD_SHARED_LIBS ON) +set(LIBSAMPLERATE_INSTALL ON) + +FetchContent_MakeAvailable(samplerate) From 592d870edbf0d6f624b05141e3f5bd35d45c88dc Mon Sep 17 00:00:00 2001 From: Ken Johnson Date: Sun, 14 Apr 2024 06:57:26 -0400 Subject: [PATCH 11/40] [IN PROGRESS COMMIT] Save audio changes --- audio/include/NovelRT/Audio/XAudio2/XAudio2AudioProvider.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/audio/include/NovelRT/Audio/XAudio2/XAudio2AudioProvider.hpp b/audio/include/NovelRT/Audio/XAudio2/XAudio2AudioProvider.hpp index 0cf8c4c46..dc38f2e6f 100644 --- a/audio/include/NovelRT/Audio/XAudio2/XAudio2AudioProvider.hpp +++ b/audio/include/NovelRT/Audio/XAudio2/XAudio2AudioProvider.hpp @@ -34,7 +34,7 @@ namespace NovelRT::Audio::XAudio2 void StopSource(uint32_t sourceId) final; void PauseSource(uint32_t sourceId) final; void SetSourceProperties(uint32_t sourceId, AudioSourceContext& context) final; - uint32_t SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, AudioSourceContext& context) final; + uint32_t SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, AudioSourceContext& context) final; AudioSourceState GetSourceState(uint32_t id) final; ~XAudio2AudioProvider() final; From c285fed0e5031054da903ff4222f8391ebafa8af Mon Sep 17 00:00:00 2001 From: Ken Johnson Date: Tue, 2 Jul 2024 16:42:35 -0400 Subject: [PATCH 12/40] Fix XAudio2 to work with floating point audio --- audio/XAudio2/XAudio2AudioProvider.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/audio/XAudio2/XAudio2AudioProvider.cpp b/audio/XAudio2/XAudio2AudioProvider.cpp index 33e78c6ba..a56570e6c 100644 --- a/audio/XAudio2/XAudio2AudioProvider.cpp +++ b/audio/XAudio2/XAudio2AudioProvider.cpp @@ -67,14 +67,14 @@ namespace NovelRT::Audio::XAudio2 { uint32_t nextSource = ++_sourceCounter; WAVEFORMATEX waveFormatContainer{}; - waveFormatContainer.wFormatTag = WAVE_FORMAT_PCM; + waveFormatContainer.wFormatTag = WAVE_FORMAT_IEEE_FLOAT; + waveFormatContainer.cbSize = 0; waveFormatContainer.nChannels = 2; + waveFormatContainer.wBitsPerSample = 32; + waveFormatContainer.nBlockAlign = 8; waveFormatContainer.nSamplesPerSec = static_cast(context.SampleRate); - waveFormatContainer.nAvgBytesPerSec = static_cast(context.SampleRate / 0.25); - waveFormatContainer.nBlockAlign = 4; - waveFormatContainer.wBitsPerSample = 16; - waveFormatContainer.cbSize = 0; - + waveFormatContainer.nAvgBytesPerSec = static_cast(waveFormatContainer.nSamplesPerSec * waveFormatContainer.nBlockAlign); + IXAudio2SourceVoice* newVoice; if(FAILED(_hr = _device->CreateSourceVoice(&newVoice, &waveFormatContainer))) @@ -108,19 +108,19 @@ namespace NovelRT::Audio::XAudio2 StopSource(sourceId); } - uint32_t XAudio2AudioProvider::SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, AudioSourceContext& context) + uint32_t XAudio2AudioProvider::SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, AudioSourceContext& context) { //uint32_t nextBuffer = ++_bufferCounter; XAUDIO2_BUFFER xABuffer = { XAUDIO2_END_OF_STREAM, // Flags - static_cast(buffer.size()*sizeof(int16_t)), // AudioBytes - static_cast(new byte[buffer.size()*sizeof(int16_t)]) //new buffer to copy int16_t* to + static_cast(buffer.size()*sizeof(float)), // AudioBytes + static_cast(new byte[buffer.size()*sizeof(float)]) //new buffer to copy float* to }; //Because XAudio2 expects a BYTE*, we'll have to cast it up and copy the data from the provided span :( std::memcpy((void*)(xABuffer.pAudioData), - reinterpret_cast(buffer.data()), buffer.size()*sizeof(int16_t)); + reinterpret_cast(buffer.data()), buffer.size()*sizeof(float)); if(context.Loop) { xABuffer.LoopCount = XAUDIO2_LOOP_INFINITE; From ca62d66bd32cef9c7c66b1b48f1ca39a1e871226 Mon Sep 17 00:00:00 2001 From: Ken Johnson Date: Sun, 5 Nov 2023 21:44:35 -0500 Subject: [PATCH 13/40] Progress commit: Compiling, untested - now submitting individual buffers to OpenAL --- audio/AudioMixer.cpp | 41 ++ audio/AudioService.cpp | 484 ------------------ audio/CMakeLists.txt | 8 +- audio/OpenAL/OpenALAudioProvider.cpp | 92 ++++ audio/include/NovelRT/Audio/Audio.hpp | 31 -- audio/include/NovelRT/Audio/AudioMixer.hpp | 25 + audio/include/NovelRT/Audio/AudioService.hpp | 75 --- .../include/NovelRT/Audio/IAudioProvider.hpp | 23 + .../Audio/OpenAL/OpenALAudioProvider.hpp | 32 ++ .../ResourceManagement/AudioMetadata.h | 4 - 10 files changed, 218 insertions(+), 597 deletions(-) create mode 100644 audio/AudioMixer.cpp delete mode 100644 audio/AudioService.cpp create mode 100644 audio/OpenAL/OpenALAudioProvider.cpp delete mode 100644 audio/include/NovelRT/Audio/Audio.hpp create mode 100644 audio/include/NovelRT/Audio/AudioMixer.hpp delete mode 100644 audio/include/NovelRT/Audio/AudioService.hpp create mode 100644 audio/include/NovelRT/Audio/IAudioProvider.hpp create mode 100644 audio/include/NovelRT/Audio/OpenAL/OpenALAudioProvider.hpp diff --git a/audio/AudioMixer.cpp b/audio/AudioMixer.cpp new file mode 100644 index 000000000..29b5d58eb --- /dev/null +++ b/audio/AudioMixer.cpp @@ -0,0 +1,41 @@ +// Copyright © Matt Jones and Contributors. Licensed under the MIT Licence (MIT). See LICENCE.md in the repository root +// for more information. +#include + +//Conditional +#include + +namespace NovelRT::Audio +{ + void AudioMixer::Initialise() + { + _audioProvider = std::make_unique(); + _audioProvider->OpenOutputStream(); + } + + void AudioMixer::SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer) + { + _audioProvider->SubmitAudioBuffer(buffer); + } + + void AudioMixer::PlayOutputStream() + { + _audioProvider->PlayOutputStream(); + } + + void AudioMixer::StopOutputStream() + { + _audioProvider->StopOutputStream(); + } + + void AudioMixer::TearDown() + { + _audioProvider->CloseOutputStream(); + _audioProvider.reset(); + } + + AudioMixer::~AudioMixer() + { + TearDown(); + } +} diff --git a/audio/AudioService.cpp b/audio/AudioService.cpp deleted file mode 100644 index 9c02d0526..000000000 --- a/audio/AudioService.cpp +++ /dev/null @@ -1,484 +0,0 @@ -// Copyright © Matt Jones and Contributors. Licensed under the MIT Licence (MIT). See LICENCE.md in the repository root -// for more information. - -#include - -namespace NovelRT::Audio -{ - AudioService::AudioService() - : _device(Utilities::Lazy>( - std::function([this] { - auto device = alcOpenDevice((_deviceName.empty()) ? nullptr : _deviceName.c_str()); - if (!device) - { - std::string error = GetALError(); - _logger.logError("OpenAL device creation failed! {}", error); - throw Exceptions::InitialisationFailureException( - "OpenAL failed to create an audio device! Aborting...", error); - } - return device; - }), - [](auto x) { alcCloseDevice(x); })), - _context(Utilities::Lazy>( - std::function([this] { - auto context = alcCreateContext(_device.getActual(), nullptr); - alcMakeContextCurrent(context); - isInitialised = true; - _deviceName = alcGetString(_device.getActual(), ALC_DEVICE_SPECIFIER); - _logger.logInfo("OpenAL Initialized on device: {}", _deviceName); - return context; - }), - [](auto x) { - alcMakeContextCurrent(nullptr); - alcDestroyContext(x); - })), - _logger(Utilities::Misc::CONSOLE_LOG_AUDIO), - _manualLoad(false), - _musicSource(), - _musicSourceState(0), - _musicStopRequested(false), - _musicLoopAmount(0), - _soundLoopAmount(0), - _soundSourceState(0), - _soundStorage(), - _bufferStorage(), - isInitialised(false) - { - } - - bool AudioService::InitializeAudio() - { - _device.getActual(); - _context.getActual(); - alGenSources(1, &_musicSource); - alSourcef(_musicSource, AL_GAIN, 0.75f); - alSourcef(_musicSource, AL_PITCH, _pitch); - - return isInitialised; - } - - ALuint AudioService::BufferAudioFrameData(NovelRT::Utilities::Misc::Span audioFrameData, - int32_t channelCount, - int32_t sampleRate) - { - ALuint buffer; - alGenBuffers(1, &buffer); - - if (buffer == _noBuffer) - { - return _noBuffer; - } - - alBufferData(buffer, channelCount == 1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16, audioFrameData.data(), - static_cast(audioFrameData.size() * sizeof(int16_t)), sampleRate); - return buffer; - } - - /*Note: Due to the current design, this will currently block the thread it is being called on. - If it is called on the main thread, please do all loading of audio files at the start of - the engine (after NovelRunner has been created). - */ - std::vector::iterator AudioService::LoadMusic(NovelRT::Utilities::Misc::Span audioFrameData, - int32_t channelCount, - int32_t sampleRate) - { - if (!isInitialised) - { - _logger.logError("Cannot load new audio into memory while the service is uninitialised! Aborting..."); - throw NovelRT::Exceptions::NotInitialisedException( - "AudioService::load", "You cannot load new audio when the service is not initialised."); - } - - auto newBuffer = BufferAudioFrameData(audioFrameData, channelCount, sampleRate); - - // Sorry Matt, nullptr types are incompatible to ALuint according to VS. - if (newBuffer == _noBuffer) - { - _logger.logWarning( - "Could not buffer provided audio data. Please verify the resource was loaded correctly."); - return _music.end(); - } - - auto it = std::find(_music.begin(), _music.end(), newBuffer); - if (it != _music.end()) - { - alDeleteBuffers(1, &newBuffer); - return it; - } - else - { - _music.push_back(newBuffer); - _bufferStorage.push_back(newBuffer); - it = std::find(_music.begin(), _music.end(), newBuffer); - return it; - } - } - - void AudioService::SetSoundVolume(ALuint source, float value) - { - if (!isInitialised) - { - _logger.logError( - "Cannot change the volume of a nonexistent sound! the service is uninitialised! Aborting..."); - throw NovelRT::Exceptions::NotInitialisedException( - "AudioService::SetSoundVolume", - "You cannot modify a sound source when the AudioService is not initialised."); - } - - if (value > 1.0f) - { - alSourcef(source, AL_GAIN, 1.0f); - } - else if (value <= 0.0f) - { - alSourcef(source, AL_GAIN, 0.0f); - } - else - { - alSourcef(source, AL_GAIN, value); - } - } - - // Switched to using two floats - for some reason VS complained when trying to use Maths::GeoVector2 here... - // This also has no effect if the buffer is more than one channel (not Mono) - void AudioService::SetSoundPosition(ALuint source, float posX, float posY) - { - if (!isInitialised) - { - _logger.logError( - "Cannot move audio position on a nonexistent sound! The service is uninitialised! Aborting..."); - throw NovelRT::Exceptions::NotInitialisedException( - "AudioService::StopSound", "You cannot stop a sound when the AudioService is not initialised."); - } - - alSource3f(source, AL_POSITION, posX, posY, 0.0f); - } - - void AudioService::ResumeMusic() - { - if (!isInitialised) - { - _logger.logError( - "Cannot change the volume of a nonexistent sound! The service is uninitialised! Aborting..."); - throw NovelRT::Exceptions::NotInitialisedException( - "AudioService::SetSoundVolume", - "You cannot modify a sound source when the AudioService is not initialised."); - } - - alSourcePlay(_musicSource); - _musicStopRequested = false; - } - - void AudioService::PlayMusic(std::vector::iterator handle, int32_t loops) - { - if (!isInitialised) - { - _logger.logError("Cannot play audio while the service is uninitialised! Aborting..."); - throw NovelRT::Exceptions::NotInitialisedException( - "AudioService::playMusic", "You cannot play a sound when the AudioService is not initialised."); - } - - if (handle == _music.end()) - { - _logger.logWarning("Cannot play the requested sound - it may have been deleted or not loaded properly."); - return; - } - - alGetSourcei(_musicSource, AL_SOURCE_STATE, &_musicSourceState); - if (_musicSourceState == AL_PLAYING) - { - alSourceStop(_musicSource); - alGetSourcei(_musicSource, AL_SOURCE_STATE, &_musicSourceState); - } - alSourcei(_musicSource, AL_BUFFER, static_cast(*handle)); - if (loops == -1 || loops > 0) - { - _musicLoopAmount = loops; - alSourcei(_musicSource, AL_LOOPING, AL_TRUE); - } - else - { - alSourcei(_musicSource, AL_LOOPING, AL_FALSE); - } - alSourcePlay(_musicSource); - _musicStopRequested = false; - } - - void AudioService::PauseMusic() - { - if (!isInitialised) - { - _logger.logError("Cannot pause audio while the service is uninitialised! Aborting..."); - throw NovelRT::Exceptions::NotInitialisedException( - "AudioService::PauseMusic", "You cannot pause a sound when the AudioService is not initialised."); - } - - _musicStopRequested = true; - alSourcePause(_musicSource); - } - - void AudioService::StopMusic() - { - if (!isInitialised) - { - _logger.logError("Cannot stop audio while the service is uninitialised! Aborting..."); - throw NovelRT::Exceptions::NotInitialisedException( - "AudioService::StopMusic", "You cannot stop a sound when the AudioService is not initialised."); - } - - _musicStopRequested = true; - alSourceStop(_musicSource); - } - - void AudioService::SetMusicVolume(float value) - { - if (!isInitialised) - { - _logger.logError("Cannot modify audio while the service is uninitialised! Aborting..."); - throw NovelRT::Exceptions::NotInitialisedException( - "AudioService::SetMusicVolume", "You cannot modify a sound when the AudioService is not initialised."); - } - - if (value > 1.0f) - { - alSourcef(_musicSource, AL_GAIN, 1.0f); - } - else if (value <= 0.0f) - { - alSourcef(_musicSource, AL_GAIN, 0.0f); - } - else - { - alSourcef(_musicSource, AL_GAIN, value); - } - } - - void AudioService::CheckSources() - { - // Changing the init check as I don't want this to kill the Runner. - if (isInitialised) - { - - int32_t musicLoop = 0; - int32_t soundLoop = 0; - for (auto sound : _soundStorage) - { - alGetSourcei(sound, AL_LOOPING, &soundLoop); - if (soundLoop == AL_TRUE) - { - alGetSourcei(sound, AL_SOURCE_STATE, &_soundSourceState); - if (_soundLoopAmount > 0) - { - _soundLoopAmount--; - if (_soundLoopAmount == 0) - { - alSourcei(sound, AL_LOOPING, AL_FALSE); - } - } - } - } - - alGetSourcei(_musicSource, AL_LOOPING, &musicLoop); - - if (musicLoop == AL_TRUE) - { - alGetSourcei(_musicSource, AL_SOURCE_STATE, &_musicSourceState); - if (_musicLoopAmount > 0 && !_musicStopRequested) - { - _musicLoopAmount--; - if (_musicLoopAmount == 0) - { - alSourcei(_musicSource, AL_LOOPING, AL_FALSE); - } - } - } - } - } - - std::string AudioService::GetALError() - { - auto err = alGetError(); - switch (err) - { - case AL_INVALID_NAME: - { - return std::string("A bad ID or name was passed to the OpenAL function."); - } - case AL_INVALID_ENUM: - { - return std::string("An invalid enum was passed to an OpenAL function."); - } - case AL_INVALID_VALUE: - { - return std::string("An invalid value was passed to an OpenAL function."); - } - case AL_INVALID_OPERATION: - { - return std::string("The requested operation is not valid."); - } - case AL_OUT_OF_MEMORY: - { - return std::string("The requested operation resulted in OpenAL running out of memory."); - } - default: - { - return std::string(""); - } - } - } - - ALuint AudioService::LoadSound(NovelRT::Utilities::Misc::Span audioFrameData, - int32_t channelCount, - int32_t sampleRate) - { - if (!isInitialised) - { - _logger.logError("Cannot load new audio into memory while the service is yolo uninitialised! Aborting..."); - throw NovelRT::Exceptions::NotInitialisedException( - "AudioService::load", "You cannot load new audio when the service is not initialised."); - } - auto newBuffer = BufferAudioFrameData(audioFrameData, channelCount, sampleRate); - - if (newBuffer == _noBuffer) - { - _logger.logWarning("Cannot play the requested sound - it may have been deleted or not loaded properly."); - return _noBuffer; - } - - _manualLoad = true; - ALuint newSource = _noBuffer; - alGenSources(1, &newSource); - alSourcef(newSource, AL_GAIN, 0.75f); - alSourcef(newSource, AL_PITCH, _pitch); - alSourcei(newSource, AL_BUFFER, static_cast(newBuffer)); - - _soundStorage.push_back(newSource); - _bufferStorage.push_back(newBuffer); - - return newSource; - } - - void AudioService::Unload(ALuint source) - { - alSourcei(source, AL_BUFFER, 0); - } - - void AudioService::PlaySound(ALuint handle, int32_t loops) - { - if (!isInitialised) - { - _logger.logError("Cannot play audio while the service is uninitialised! Aborting..."); - throw NovelRT::Exceptions::NotInitialisedException( - "AudioService::playMusic", "You cannot play a sound when the AudioService is not initialised."); - } - - if (handle == _noBuffer) - { - _logger.logError("Cannot play the requested sound - it may have been deleted or not loaded properly."); - return; - } - - if (loops == -1 || loops > 0) - { - _soundLoopAmount = loops; - alSourcei(handle, AL_LOOPING, AL_TRUE); - } - else - { - alSourcei(handle, AL_LOOPING, AL_FALSE); - } - alSourcePlay(handle); - } - - void AudioService::StopSound(ALuint handle) - { - alSourceStop(handle); - } - - bool AudioService::IsLoaded(std::vector::iterator handle) - { - return (handle != _music.end()); - } - - bool AudioService::IsLoaded(ALuint handle) - { - return (handle != _noBuffer); - } - - void AudioService::TearDown() - { - if (!_context.isCreated()) - return; - - if (_manualLoad) - { - for (auto source : _soundStorage) - { - alDeleteSources(1, &source); - } - _soundStorage.clear(); - } - - alDeleteSources(1, &_musicSource); - if (!_music.empty()) - { - _music.clear(); - } - - for (auto buffer : _bufferStorage) - { - alDeleteBuffers(1, &buffer); - } - - // were deleting the objects explicitly here to ensure they're always deleted in the right order, lest you - // summon the kraken. - Ruby - _context.reset(); - _device.reset(); - } - - AudioService::~AudioService() - { - TearDown(); - } - - bool AudioService::IsMusicPlaying() - { - alGetSourcei(_musicSource, AL_SOURCE_STATE, &_musicSourceState); - return (_musicSourceState == AL_PLAYING || _musicSourceState == AL_PAUSED); - } - - bool AudioService::IsSoundPlaying(ALuint handle) - { - alGetSourcei(handle, AL_SOURCE_STATE, &_soundSourceState); - return (_soundSourceState == AL_PLAYING || _soundSourceState == AL_PAUSED); - } - - float AudioService::GetMusicVolume() - { - if (!isInitialised) - { - _logger.logError("Cannot modify audio while the service is uninitialised! Aborting..."); - throw NovelRT::Exceptions::NotInitialisedException( - "AudioService::SetMusicVolume", "You cannot modify a sound when the AudioService is not initialised."); - } - - float result = 0.0f; - alGetSourcef(_musicSource, AL_GAIN, &result); - return result; - } - - float AudioService::GetSoundVolume(ALuint handle) - { - if (!isInitialised) - { - _logger.logError("Cannot modify audio while the service is uninitialised! Aborting..."); - throw NovelRT::Exceptions::NotInitialisedException( - "AudioService::SetMusicVolume", "You cannot modify a sound when the AudioService is not initialised."); - } - - float result = 0.0f; - alGetSourcef(handle, AL_GAIN, &result); - return result; - } - -} // namespace NovelRT::Audio diff --git a/audio/CMakeLists.txt b/audio/CMakeLists.txt index 8fdb940a5..1d873f634 100644 --- a/audio/CMakeLists.txt +++ b/audio/CMakeLists.txt @@ -1,5 +1,6 @@ add_library(NovelRT-Audio STATIC - AudioService.cpp + AudioMixer.cpp + OpenAL/OpenALAudioProvider.cpp ) target_sources(NovelRT-Audio @@ -8,8 +9,9 @@ target_sources(NovelRT-Audio TYPE HEADERS BASE_DIRS include FILES - include/NovelRT/Audio/Audio.hpp - include/NovelRT/Audio/AudioService.hpp + include/NovelRT/Audio/AudioMixer.hpp + include/NovelRT/Audio/IAudioProvider.hpp + include/NovelRT/Audio/OpenAL/OpenALAudioProvider.hpp ) set_target_properties(NovelRT-Audio diff --git a/audio/OpenAL/OpenALAudioProvider.cpp b/audio/OpenAL/OpenALAudioProvider.cpp new file mode 100644 index 000000000..2f2ed44aa --- /dev/null +++ b/audio/OpenAL/OpenALAudioProvider.cpp @@ -0,0 +1,92 @@ +// Copyright © Matt Jones and Contributors. Licensed under the MIT Licence (MIT). See LICENCE.md in the repository root +// for more information. +#include +#include + +namespace NovelRT::Audio::OpenAL +{ + OpenALAudioProvider::OpenALAudioProvider() + { + //Device and Context Init + _device = alcOpenDevice(nullptr); + if(!_device) + { + std::string error = GetALError(); + throw Exceptions::InitialisationFailureException( + "OpenAL failed to create an audio device!", error); + } + _context = alcCreateContext(_device, nullptr); + alcMakeContextCurrent(_context); + + OpenOutputStream(); + } + + void OpenALAudioProvider::Dispose() + { + CloseOutputStream(); + + + } + + void OpenALAudioProvider::OpenOutputStream() + { + alGenSources(1, &_outputSource); + alSourcef(_outputSource, AL_GAIN, 0.75f); + alSourcef(_outputSource, AL_PITCH, 1.0f); + } + + void OpenALAudioProvider::CloseOutputStream() + { + alDeleteSources(1, &_outputSource); + } + + void OpenALAudioProvider::PlayOutputStream() + { + + } + + void OpenALAudioProvider::StopOutputStream() + { + + } + + std::string OpenALAudioProvider::GetALError() + { + auto err = alGetError(); + switch (err) + { + case AL_INVALID_NAME: + { + return std::string("A bad ID or name was passed to the OpenAL function."); + } + case AL_INVALID_ENUM: + { + return std::string("An invalid enum was passed to an OpenAL function."); + } + case AL_INVALID_VALUE: + { + return std::string("An invalid value was passed to an OpenAL function."); + } + case AL_INVALID_OPERATION: + { + return std::string("The requested operation is not valid."); + } + case AL_OUT_OF_MEMORY: + { + return std::string("The requested operation resulted in OpenAL running out of memory."); + } + default: + { + return std::string(""); + } + } + } + + void OpenALAudioProvider::SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer) + { + ALuint alBuffer; + alGenBuffers(1, &alBuffer); + alBufferData(alBuffer, AL_FORMAT_STEREO16, buffer.data(), static_cast(buffer.size() * sizeof(int16_t)), 44100); + alSourcei(_outputSource, AL_BUFFER, alBuffer); + } +} diff --git a/audio/include/NovelRT/Audio/Audio.hpp b/audio/include/NovelRT/Audio/Audio.hpp deleted file mode 100644 index 79653a3aa..000000000 --- a/audio/include/NovelRT/Audio/Audio.hpp +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright © Matt Jones and Contributors. Licensed under the MIT Licence (MIT). See LICENCE.md in the repository root -// for more information. - -#ifndef NOVELRT_AUDIO_H -#define NOVELRT_AUDIO_H - -// Dependencies for Audio -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/*** - * @brief Contains audio features, such as playing audio, and managing audio resources. - */ -namespace NovelRT::Audio -{ - typedef std::vector SoundBank; - typedef std::vector MusicBank; - typedef class AudioService AudioService; -} - -// Audio Types -#include - -#endif // NOVELRT_AUDIO_H diff --git a/audio/include/NovelRT/Audio/AudioMixer.hpp b/audio/include/NovelRT/Audio/AudioMixer.hpp new file mode 100644 index 000000000..a8f2f9a23 --- /dev/null +++ b/audio/include/NovelRT/Audio/AudioMixer.hpp @@ -0,0 +1,25 @@ +// Copyright © Matt Jones and Contributors. Licensed under the MIT Licence (MIT). See LICENCE.md in the repository root +// for more information. +#pragma once + +#include +#include +#include +#include + +namespace NovelRT::Audio +{ + class AudioMixer + { + private: + std::unique_ptr _audioProvider; + void TearDown(); + + public: + void Initialise(); + void SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer); + void PlayOutputStream(); + void StopOutputStream(); + ~AudioMixer(); + }; +} diff --git a/audio/include/NovelRT/Audio/AudioService.hpp b/audio/include/NovelRT/Audio/AudioService.hpp deleted file mode 100644 index 8e4689852..000000000 --- a/audio/include/NovelRT/Audio/AudioService.hpp +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright © Matt Jones and Contributors. Licensed under the MIT Licence (MIT). See LICENCE.md in the repository root -// for more information. - -#ifndef NOVELRT_AUDIO_AUDIOSERVICE_H -#define NOVELRT_AUDIO_AUDIOSERVICE_H - -#ifndef NOVELRT_AUDIO_H -#error NovelRT does not support including types explicitly by default. Please include Audio.h instead for the Audio namespace subset. -#endif - -namespace NovelRT::Audio -{ - // TODO: This won't exist after Kenny's rewrite, not too bothered about this class. - class AudioService - { - private: - const ALuint _noBuffer = 0; - const ALfloat _pitch = 1.0f; - - Utilities::Lazy> _device; - Utilities::Lazy> _context; - std::string _deviceName; - LoggingService _logger; - bool _manualLoad; - MusicBank _music; - ALuint _musicSource; - ALint _musicSourceState; - bool _musicStopRequested; - ALint _musicLoopAmount; - ALint _soundLoopAmount; - ALint _soundSourceState; - SoundBank _soundStorage; - SoundBank _bufferStorage; - - ALuint BufferAudioFrameData(NovelRT::Utilities::Misc::Span audioFrameData, - int32_t channelCount, - int32_t sampleRate); - std::string GetALError(); - - public: - bool isInitialised; - - AudioService(); - ~AudioService(); - - bool InitializeAudio(); - std::vector::iterator LoadMusic(NovelRT::Utilities::Misc::Span audioFrameData, - int32_t channelCount, - int32_t sampleRate); - - void SetSoundVolume(ALuint source, float val); - void SetSoundPosition(ALuint source, float posX, float posY); - void ResumeMusic(); - void PlayMusic(std::vector::iterator handle, int32_t loops); - void PauseMusic(); - void StopMusic(); - void SetMusicVolume(float value); - void CheckSources(); - ALuint LoadSound(NovelRT::Utilities::Misc::Span audioFrameData, - int32_t channelCount, - int32_t sampleRate); - void Unload(ALuint handle); - void PlaySound(ALuint handle, int32_t loops); - void StopSound(ALuint handle); - void TearDown(); - [[nodiscard]] bool IsLoaded(std::vector::iterator handle); - [[nodiscard]] bool IsLoaded(ALuint handle); - [[nodiscard]] bool IsMusicPlaying(); - [[nodiscard]] bool IsSoundPlaying(ALuint handle); - [[nodiscard]] float GetMusicVolume(); - [[nodiscard]] float GetSoundVolume(ALuint handle); - }; -} - -#endif // NOVELRT_AUDIO_AUDIOSERVICE_H diff --git a/audio/include/NovelRT/Audio/IAudioProvider.hpp b/audio/include/NovelRT/Audio/IAudioProvider.hpp new file mode 100644 index 000000000..b3bed3e54 --- /dev/null +++ b/audio/include/NovelRT/Audio/IAudioProvider.hpp @@ -0,0 +1,23 @@ +// Copyright © Matt Jones and Contributors. Licensed under the MIT Licence (MIT). See LICENCE.md in the repository root +// for more information. +#pragma once + +#include + +namespace NovelRT::Audio +{ + class IAudioProvider + { + protected: + virtual void Dispose() = 0; + + public: + virtual void OpenOutputStream() = 0; + virtual void CloseOutputStream() = 0; + virtual void SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer) = 0; + virtual void PlayOutputStream() = 0; + virtual void StopOutputStream() = 0; + virtual ~IAudioProvider() = 0; + + }; +} diff --git a/audio/include/NovelRT/Audio/OpenAL/OpenALAudioProvider.hpp b/audio/include/NovelRT/Audio/OpenAL/OpenALAudioProvider.hpp new file mode 100644 index 000000000..35cb18efd --- /dev/null +++ b/audio/include/NovelRT/Audio/OpenAL/OpenALAudioProvider.hpp @@ -0,0 +1,32 @@ +// Copyright © Matt Jones and Contributors. Licensed under the MIT Licence (MIT). See LICENCE.md in the repository root +// for more information. +#pragma once + +#include +#include +#include + +namespace NovelRT::Audio::OpenAL +{ + class OpenALAudioProvider : public IAudioProvider + { + private: + ALuint _outputSource; + ALCdevice* _device; + ALCcontext* _context; + + std::string GetALError(); + + protected: + void Dispose() final; + + public: + OpenALAudioProvider(); + + void OpenOutputStream() final; + void CloseOutputStream() final; + void PlayOutputStream() final; + void StopOutputStream() final; + void SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer) final; + }; +} diff --git a/include/NovelRT/ResourceManagement/AudioMetadata.h b/include/NovelRT/ResourceManagement/AudioMetadata.h index f126f9480..17355fdec 100644 --- a/include/NovelRT/ResourceManagement/AudioMetadata.h +++ b/include/NovelRT/ResourceManagement/AudioMetadata.h @@ -4,10 +4,6 @@ #ifndef NOVELRT_RESOURCEMANAGEMENT_AUDIOMETADATA_H #define NOVELRT_RESOURCEMANAGEMENT_AUDIOMETADATA_H -#ifndef NOVELRT_RESOURCEMANAGEMENT_H -#error NovelRT does not support including types explicitly by default. Please include ResourceManagement.h instead for the ResourceManagement namespace subset. -#endif - namespace NovelRT::ResourceManagement { struct AudioMetadata From 0db41943433841b55db928826f29a613add801b5 Mon Sep 17 00:00:00 2001 From: Ken Johnson Date: Fri, 16 Feb 2024 20:52:31 -0500 Subject: [PATCH 14/40] Fix spdlog to use external fmt Resolves an issue where spdlog's bundled fmt lib was using deprecated/non-standard MSFT extensions that Windows was removing soon --- thirdparty/CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/thirdparty/CMakeLists.txt b/thirdparty/CMakeLists.txt index fb65e2eda..a221e601f 100644 --- a/thirdparty/CMakeLists.txt +++ b/thirdparty/CMakeLists.txt @@ -103,7 +103,9 @@ thirdparty_module(ZLIB # N.B. Order matters here, for dependency searching! foreach(module - nlohmann_json Fabulist + nlohmann_json + Fabulist + fmt glfw3 glm GSL From d02bb398f9833908a59609a2475f0781799b8d4e Mon Sep 17 00:00:00 2001 From: Ken Johnson Date: Sat, 17 Feb 2024 08:31:27 -0500 Subject: [PATCH 15/40] OpenAL partially working No audio playing due to bad handling of buffers --- audio/AudioMixer.cpp | 61 ++- audio/OpenAL/OpenALAudioProvider.cpp | 88 +++- audio/include/NovelRT/Audio/AudioMixer.hpp | 18 +- .../NovelRT/Audio/AudioSourceContext.hpp | 16 + .../NovelRT/Audio/AudioSourceState.hpp | 15 + .../include/NovelRT/Audio/IAudioProvider.hpp | 17 +- .../Audio/OpenAL/OpenALAudioProvider.hpp | 17 +- include/NovelRT/Ecs/Audio/AudioSystem.h | 10 +- include/NovelRT/Ecs/Audio/Ecs.Audio.h | 8 +- samples/AudioEcsSample/main.cpp | 4 +- src/NovelRT/Ecs/Audio/AudioSystem.cpp | 411 ++++++++---------- 11 files changed, 379 insertions(+), 286 deletions(-) create mode 100644 audio/include/NovelRT/Audio/AudioSourceContext.hpp create mode 100644 audio/include/NovelRT/Audio/AudioSourceState.hpp diff --git a/audio/AudioMixer.cpp b/audio/AudioMixer.cpp index 29b5d58eb..55020326a 100644 --- a/audio/AudioMixer.cpp +++ b/audio/AudioMixer.cpp @@ -9,28 +9,73 @@ namespace NovelRT::Audio { void AudioMixer::Initialise() { + _sourceContextCache = std::map(); _audioProvider = std::make_unique(); - _audioProvider->OpenOutputStream(); } - void AudioMixer::SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer) + uint32_t AudioMixer::SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span& buffer) { - _audioProvider->SubmitAudioBuffer(buffer); + auto newContext = AudioSourceContext(); + uint32_t sourceId = _audioProvider->SubmitAudioBuffer(buffer, newContext); + _sourceContextCache.emplace(sourceId, newContext); + return sourceId; } - void AudioMixer::PlayOutputStream() + AudioSourceState AudioMixer::GetSourceState(uint32_t id) { - _audioProvider->PlayOutputStream(); + return _audioProvider->GetSourceState(id); } - void AudioMixer::StopOutputStream() + void AudioMixer::PlaySource(uint32_t id) { - _audioProvider->StopOutputStream(); + _audioProvider->PlaySource(id); + } + + void AudioMixer::StopSource(uint32_t id) + { + _audioProvider->StopSource(id); + } + + void AudioMixer::PauseSource(uint32_t id) + { + _audioProvider->PauseSource(id); + } + + AudioSourceContext& AudioMixer::GetSourceContext(uint32_t id) + { + return _sourceContextCache.at(id); + } + + void AudioMixer::SetSourceContext(uint32_t id, AudioSourceContext& context) + { + _sourceContextCache.erase(id); + _sourceContextCache.emplace(id, context); + _audioProvider->SetSourceProperties(id, context); + } + + void AudioMixer::SetSourceVolume(uint32_t id, float volume) + { + auto& context = _sourceContextCache.at(id); + context.Volume = volume; + _audioProvider->SetSourceProperties(id, context); + } + + void AudioMixer::SetSourcePitch(uint32_t id, float pitch) + { + auto& context = _sourceContextCache.at(id); + context.Pitch = pitch; + _audioProvider->SetSourceProperties(id, context); + } + + void AudioMixer::SetSourceLoop(uint32_t id, bool isLooping) + { + auto& context = _sourceContextCache.at(id); + context.Loop = isLooping; + _audioProvider->SetSourceProperties(id, context); } void AudioMixer::TearDown() { - _audioProvider->CloseOutputStream(); _audioProvider.reset(); } diff --git a/audio/OpenAL/OpenALAudioProvider.cpp b/audio/OpenAL/OpenALAudioProvider.cpp index 2f2ed44aa..5bf3661e5 100644 --- a/audio/OpenAL/OpenALAudioProvider.cpp +++ b/audio/OpenAL/OpenALAudioProvider.cpp @@ -7,6 +7,9 @@ namespace NovelRT::Audio::OpenAL { OpenALAudioProvider::OpenALAudioProvider() { + _buffers = std::vector(); + _sources = std::vector(); + //Device and Context Init _device = alcOpenDevice(nullptr); if(!_device) @@ -17,37 +20,52 @@ namespace NovelRT::Audio::OpenAL } _context = alcCreateContext(_device, nullptr); alcMakeContextCurrent(_context); + } - OpenOutputStream(); + OpenALAudioProvider::~OpenALAudioProvider() + { + Dispose(); } void OpenALAudioProvider::Dispose() { - CloseOutputStream(); - - + alSourceStopv(static_cast(_sources.size()), _sources.data()); + alDeleteSources(static_cast(_sources.size()), _sources.data()); + _sources.clear(); + alDeleteBuffers(static_cast(_buffers.size()), _buffers.data()); + _buffers.clear(); + alcDestroyContext(_context); + alcCloseDevice(_device); } - void OpenALAudioProvider::OpenOutputStream() + uint32_t OpenALAudioProvider::OpenSource(AudioSourceContext& context) { - alGenSources(1, &_outputSource); - alSourcef(_outputSource, AL_GAIN, 0.75f); - alSourcef(_outputSource, AL_PITCH, 1.0f); + uint32_t source = 0; + alGenSources(1, &source); + alSourcef(source, AL_GAIN, context.Volume); + alSourcef(source, AL_PITCH, context.Pitch); + alSourcei(source, AL_LOOPING, static_cast(context.Loop)); + return source; } - void OpenALAudioProvider::CloseOutputStream() + // void OpenALAudioProvider::CloseSource() + // { + // alDeleteSources(1, &_outputSource); + // } + + void OpenALAudioProvider::PlaySource(uint32_t sourceId) { - alDeleteSources(1, &_outputSource); + alSourcePlay(sourceId); } - void OpenALAudioProvider::PlayOutputStream() + void OpenALAudioProvider::StopSource(uint32_t sourceId) { - + alSourceStop(sourceId); } - void OpenALAudioProvider::StopOutputStream() + void OpenALAudioProvider::PauseSource(uint32_t sourceId) { - + alSourcePause(sourceId); } std::string OpenALAudioProvider::GetALError() @@ -82,11 +100,49 @@ namespace NovelRT::Audio::OpenAL } } - void OpenALAudioProvider::SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer) + uint32_t OpenALAudioProvider::SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span& buffer, AudioSourceContext& context) { ALuint alBuffer; alGenBuffers(1, &alBuffer); alBufferData(alBuffer, AL_FORMAT_STEREO16, buffer.data(), static_cast(buffer.size() * sizeof(int16_t)), 44100); - alSourcei(_outputSource, AL_BUFFER, alBuffer); + uint32_t sourceId = OpenSource(context); + alSourcei(sourceId, AL_BUFFER, alBuffer); + return sourceId; + } + + void OpenALAudioProvider::SetSourceProperties(uint32_t sourceId, AudioSourceContext& context) + { + alSourcef(sourceId, AL_GAIN, context.Volume); + alSourcef(sourceId, AL_PITCH, context.Pitch); + alSourcei(sourceId, AL_LOOPING, static_cast(context.Loop)); } + + AudioSourceState OpenALAudioProvider::ConvertToAudioSourceState(ALenum oALSourceState) + { + switch(oALSourceState) + { + case AL_PLAYING: + { + return AudioSourceState::SOURCE_PLAYING; + } + case AL_PAUSED: + { + return AudioSourceState::SOURCE_PAUSED; + } + case AL_STOPPED: + case AL_INITIAL: + default: + { + return AudioSourceState::SOURCE_STOPPED; + } + } + } + + AudioSourceState OpenALAudioProvider::GetSourceState(uint32_t sourceId) + { + ALenum state = 0x0; + alGetSourcei(sourceId, AL_SOURCE_STATE, &state); + return ConvertToAudioSourceState(state); + } + } diff --git a/audio/include/NovelRT/Audio/AudioMixer.hpp b/audio/include/NovelRT/Audio/AudioMixer.hpp index a8f2f9a23..5f7a6c101 100644 --- a/audio/include/NovelRT/Audio/AudioMixer.hpp +++ b/audio/include/NovelRT/Audio/AudioMixer.hpp @@ -4,7 +4,10 @@ #include #include +#include #include +#include +#include #include namespace NovelRT::Audio @@ -13,13 +16,22 @@ namespace NovelRT::Audio { private: std::unique_ptr _audioProvider; + std::map _sourceContextCache; void TearDown(); public: void Initialise(); - void SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer); - void PlayOutputStream(); - void StopOutputStream(); + uint32_t SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span& buffer); + void PlaySource(uint32_t id); + void StopSource(uint32_t id); + void PauseSource(uint32_t id); + AudioSourceContext& GetSourceContext(uint32_t id); + void SetSourceContext(uint32_t id, AudioSourceContext& context); + void SetSourceVolume(uint32_t id, float volume); + void SetSourcePitch(uint32_t id, float pitch); + void SetSourceLoop(uint32_t id, bool isLooping); + AudioSourceState GetSourceState(uint32_t id); + ~AudioMixer(); }; } diff --git a/audio/include/NovelRT/Audio/AudioSourceContext.hpp b/audio/include/NovelRT/Audio/AudioSourceContext.hpp new file mode 100644 index 000000000..690399cd2 --- /dev/null +++ b/audio/include/NovelRT/Audio/AudioSourceContext.hpp @@ -0,0 +1,16 @@ +// Copyright © Matt Jones and Contributors. Licensed under the MIT Licence (MIT). See LICENCE.md in the repository root +// for more information. +#pragma once + +#include + +namespace NovelRT::Audio +{ + struct AudioSourceContext + { + public: + float Volume = 0.75f; + float Pitch = 1.0f; + bool Loop = false; + }; +} diff --git a/audio/include/NovelRT/Audio/AudioSourceState.hpp b/audio/include/NovelRT/Audio/AudioSourceState.hpp new file mode 100644 index 000000000..e683493a2 --- /dev/null +++ b/audio/include/NovelRT/Audio/AudioSourceState.hpp @@ -0,0 +1,15 @@ +// Copyright © Matt Jones and Contributors. Licensed under the MIT Licence (MIT). See LICENCE.md in the repository root +// for more information. +#pragma once + +#include + +namespace NovelRT::Audio +{ + enum class AudioSourceState: int32_t + { + SOURCE_STOPPED = 0, + SOURCE_PAUSED = 1, + SOURCE_PLAYING = 2 + }; +} diff --git a/audio/include/NovelRT/Audio/IAudioProvider.hpp b/audio/include/NovelRT/Audio/IAudioProvider.hpp index b3bed3e54..bcfdde785 100644 --- a/audio/include/NovelRT/Audio/IAudioProvider.hpp +++ b/audio/include/NovelRT/Audio/IAudioProvider.hpp @@ -3,6 +3,8 @@ #pragma once #include +#include +#include namespace NovelRT::Audio { @@ -10,14 +12,17 @@ namespace NovelRT::Audio { protected: virtual void Dispose() = 0; + virtual uint32_t OpenSource(AudioSourceContext& context) = 0; public: - virtual void OpenOutputStream() = 0; - virtual void CloseOutputStream() = 0; - virtual void SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer) = 0; - virtual void PlayOutputStream() = 0; - virtual void StopOutputStream() = 0; - virtual ~IAudioProvider() = 0; + virtual uint32_t SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span& buffer, AudioSourceContext& context) = 0; + virtual void PlaySource(uint32_t sourceId) = 0; + virtual void StopSource(uint32_t sourceId) = 0; + virtual void PauseSource(uint32_t sourceId) = 0; + virtual void SetSourceProperties(uint32_t sourceId, AudioSourceContext& context) = 0; + virtual AudioSourceState GetSourceState(uint32_t id) = 0; + + virtual ~IAudioProvider() = default; }; } diff --git a/audio/include/NovelRT/Audio/OpenAL/OpenALAudioProvider.hpp b/audio/include/NovelRT/Audio/OpenAL/OpenALAudioProvider.hpp index 35cb18efd..e73fb6d67 100644 --- a/audio/include/NovelRT/Audio/OpenAL/OpenALAudioProvider.hpp +++ b/audio/include/NovelRT/Audio/OpenAL/OpenALAudioProvider.hpp @@ -5,6 +5,7 @@ #include #include #include +#include namespace NovelRT::Audio::OpenAL { @@ -14,19 +15,25 @@ namespace NovelRT::Audio::OpenAL ALuint _outputSource; ALCdevice* _device; ALCcontext* _context; + std::vector _sources; + std::vector _buffers; std::string GetALError(); + AudioSourceState ConvertToAudioSourceState(ALenum oalSourceState); protected: void Dispose() final; + uint32_t OpenSource(AudioSourceContext& context) final; public: OpenALAudioProvider(); + void PlaySource(uint32_t sourceId) final; + void StopSource(uint32_t sourceId) final; + void PauseSource(uint32_t sourceId) final; + void SetSourceProperties(uint32_t sourceId, AudioSourceContext& context) final; + uint32_t SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span& buffer, AudioSourceContext& context) final; + AudioSourceState GetSourceState(uint32_t id) final; - void OpenOutputStream() final; - void CloseOutputStream() final; - void PlayOutputStream() final; - void StopOutputStream() final; - void SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer) final; + ~OpenALAudioProvider() final; }; } diff --git a/include/NovelRT/Ecs/Audio/AudioSystem.h b/include/NovelRT/Ecs/Audio/AudioSystem.h index 78ed4e5e0..2b84a5476 100644 --- a/include/NovelRT/Ecs/Audio/AudioSystem.h +++ b/include/NovelRT/Ecs/Audio/AudioSystem.h @@ -14,22 +14,20 @@ namespace NovelRT::Ecs::Audio { private: uint32_t _counter; - std::map> _fadeCache; LoggingService _logger; - std::map::iterator> _musicCache; - std::shared_ptr _service; - std::map _soundCache; + std::vector _soundCache; + std::shared_ptr _mixer; NovelRT::Timing::Timestamp _systemTime; std::shared_ptr _resourceManagerPluginProvider; - void ChangeAudioVolume(AudioEmitterComponent emitter, float desiredVolume); + // void ChangeAudioVolume(AudioEmitterComponent emitter, float desiredVolume); public: AudioSystem(std::shared_ptr resourceManagerPluginProvider); ~AudioSystem() noexcept; void Update(Timing::Timestamp delta, Ecs::Catalogue catalogue) final; - uint32_t CreateAudio(std::string fileName, bool isMusic); + uint32_t RegisterSound(std::string fileName); }; } diff --git a/include/NovelRT/Ecs/Audio/Ecs.Audio.h b/include/NovelRT/Ecs/Audio/Ecs.Audio.h index dad3e5cbc..96b621178 100644 --- a/include/NovelRT/Ecs/Audio/Ecs.Audio.h +++ b/include/NovelRT/Ecs/Audio/Ecs.Audio.h @@ -8,9 +8,11 @@ #error NovelRT does not support including types explicitly by default. Please include Ecs.h instead for the Ecs namespace subset. #endif -#include "../../Timing/StepTimer.h" -#include "../../Timing/Timestamp.h" -#include +#include +#include +#include +#include + #include #include diff --git a/samples/AudioEcsSample/main.cpp b/samples/AudioEcsSample/main.cpp index 37eecd0d3..3fd111341 100644 --- a/samples/AudioEcsSample/main.cpp +++ b/samples/AudioEcsSample/main.cpp @@ -41,12 +41,12 @@ int main() // Create the sound components std::string uwu = (soundDir / "uwu.ogg").string(); - auto uwuHandle = audioSystem->CreateAudio(uwu, true); + auto uwuHandle = audioSystem->RegisterSound(uwu); AudioEmitterComponent uwuComponent = AudioEmitterComponent{uwuHandle, true, -1, 0.75f}; AudioEmitterStateComponent uwuState = AudioEmitterStateComponent{AudioEmitterState::ToFadeIn, 8.0f, 0.75f}; std::string ahh = (soundDir / "goat.wav").string(); - auto goatHandle = audioSystem->CreateAudio(ahh, false); + auto goatHandle = audioSystem->RegisterSound(ahh); AudioEmitterComponent goatComponent = AudioEmitterComponent{goatHandle, false, 0, 0.75f}; AudioEmitterStateComponent goatState = AudioEmitterStateComponent{AudioEmitterState::Stopped, 0, 0}; diff --git a/src/NovelRT/Ecs/Audio/AudioSystem.cpp b/src/NovelRT/Ecs/Audio/AudioSystem.cpp index f2ac17b1a..2111e6f57 100644 --- a/src/NovelRT/Ecs/Audio/AudioSystem.cpp +++ b/src/NovelRT/Ecs/Audio/AudioSystem.cpp @@ -8,15 +8,13 @@ namespace NovelRT::Ecs::Audio AudioSystem::AudioSystem( std::shared_ptr resourceManagerPluginProvider) : _counter(1), - _fadeCache(std::map>()), _logger(Utilities::Misc::CONSOLE_LOG_AUDIO), - _musicCache(std::map::iterator>()), - _service(std::make_shared()), - _soundCache(std::map()), + _mixer(std::make_shared()), + _soundCache(std::vector()), _systemTime(Timing::Timestamp::zero()), _resourceManagerPluginProvider(std::move(resourceManagerPluginProvider)) { - _service->InitializeAudio(); + _mixer->Initialise(); } void AudioSystem::Update(Timing::Timestamp delta, Ecs::Catalogue catalogue) @@ -32,14 +30,7 @@ namespace NovelRT::Ecs::Audio { case AudioEmitterState::ToPlay: { - if (emitter.isMusic) - { - _service->PlayMusic(_musicCache.at(emitter.handle), emitter.numberOfLoops); - } - else - { - _service->PlaySound(_soundCache.at(emitter.handle), emitter.numberOfLoops); - } + _mixer->PlaySource(emitter.handle); _logger.logDebug("Entity ID {} - EmitterState ToPlay -> Playing", entity); states.PushComponentUpdateInstruction( entity, AudioEmitterStateComponent{AudioEmitterState::Playing, emitterState.fadeDuration}); @@ -47,14 +38,7 @@ namespace NovelRT::Ecs::Audio } case AudioEmitterState::ToStop: { - if (emitter.isMusic) - { - _service->StopMusic(); - } - else - { - _service->StopSound(_soundCache.at(emitter.handle)); - } + _mixer->StopSource(emitter.handle); _logger.logDebug("Entity ID {} - EmitterState ToStop -> Stopped", entity); states.PushComponentUpdateInstruction( entity, AudioEmitterStateComponent{AudioEmitterState::Stopped, emitterState.fadeDuration}); @@ -62,197 +46,172 @@ namespace NovelRT::Ecs::Audio } case AudioEmitterState::ToPause: { - if (emitter.isMusic) - { - _service->PauseMusic(); - _logger.logDebug("Entity ID {} - EmitterState ToPause -> Paused", entity); - states.PushComponentUpdateInstruction( - entity, AudioEmitterStateComponent{AudioEmitterState::Paused, emitterState.fadeDuration}); - } + _mixer->PauseSource(emitter.handle); + _logger.logDebug("Entity ID {} - EmitterState ToPause -> Paused", entity); + states.PushComponentUpdateInstruction( + entity, AudioEmitterStateComponent{AudioEmitterState::Paused, emitterState.fadeDuration}); break; } case AudioEmitterState::ToResume: { - if (emitter.isMusic) - { - _service->ResumeMusic(); - _logger.logDebug("Entity ID {} - EmitterState ToResume -> Playing", entity); - states.PushComponentUpdateInstruction( - entity, AudioEmitterStateComponent{AudioEmitterState::Playing, emitterState.fadeDuration}); - } + _mixer->PlaySource(emitter.handle); + _logger.logDebug("Entity ID {} - EmitterState ToResume -> Playing", entity); + states.PushComponentUpdateInstruction( + entity, AudioEmitterStateComponent{AudioEmitterState::Playing, emitterState.fadeDuration}); break; } - case AudioEmitterState::ToFadeOut: - { - if (emitter.isMusic && !_service->IsMusicPlaying()) - { - states.PushComponentUpdateInstruction( - entity, AudioEmitterStateComponent{AudioEmitterState::Stopped, 0.0f, 0.0f}); - break; - } - else if (!emitter.isMusic && !_service->IsSoundPlaying(_soundCache.at(emitter.handle))) - { - states.PushComponentUpdateInstruction( - entity, AudioEmitterStateComponent{AudioEmitterState::Stopped, 0.0f, 0.0f}); - break; - } + // case AudioEmitterState::ToFadeOut: + // { + // if (emitter.isMusic && !_mixer->IsMusicPlaying()) + // { + // states.PushComponentUpdateInstruction( + // entity, AudioEmitterStateComponent{AudioEmitterState::Stopped, 0.0f, 0.0f}); + // break; + // } + // else if (!emitter.isMusic && !_mixer->IsSoundPlaying(_soundCache.at(emitter.handle))) + // { + // states.PushComponentUpdateInstruction( + // entity, AudioEmitterStateComponent{AudioEmitterState::Stopped, 0.0f, 0.0f}); + // break; + // } - float slope = -(emitter.volume / emitterState.fadeDuration); - Timing::Timestamp endTime = _systemTime + Timing::Timestamp::fromSeconds(emitterState.fadeDuration); - _fadeCache.insert({entity, std::make_tuple(endTime, slope)}); + // float slope = -(emitter.volume / emitterState.fadeDuration); + // Timing::Timestamp endTime = _systemTime + Timing::Timestamp::fromSeconds(emitterState.fadeDuration); + // _fadeCache.insert({entity, std::make_tuple(endTime, slope)}); - states.PushComponentUpdateInstruction( - entity, - AudioEmitterStateComponent{AudioEmitterState::FadingOut, emitterState.fadeDuration, 0.0f}); - _logger.logDebug("Entity ID {} - EmitterState ToFadeOut -> FadingOut", entity); - break; - } - case AudioEmitterState::FadingOut: - { - Timing::Timestamp endTime = std::get<0>(_fadeCache.at(entity)); - float remainingDuration = (endTime - _systemTime).getSecondsFloat(); + // states.PushComponentUpdateInstruction( + // entity, + // AudioEmitterStateComponent{AudioEmitterState::FadingOut, emitterState.fadeDuration, 0.0f}); + // _logger.logDebug("Entity ID {} - EmitterState ToFadeOut -> FadingOut", entity); + // break; + // } + // case AudioEmitterState::FadingOut: + // { + // Timing::Timestamp endTime = std::get<0>(_fadeCache.at(entity)); + // float remainingDuration = (endTime - _systemTime).getSecondsFloat(); - if (_systemTime < endTime) - { - float slope = std::get<1>(_fadeCache.at(entity)); - float newVolume = emitter.volume + (slope * delta.getSecondsFloat()); - ChangeAudioVolume(emitter, newVolume); - states.PushComponentUpdateInstruction( - entity, AudioEmitterStateComponent{AudioEmitterState::FadingOut, remainingDuration, - emitterState.fadeExpectedVolume}); - emitters.PushComponentUpdateInstruction( - entity, - AudioEmitterComponent{emitter.handle, emitter.isMusic, emitter.numberOfLoops, newVolume}); - _logger.logDebug("Entity ID {} - EmitterState FadingOut at volume {}, slope: {}, currentTime: " - "{}, remaining: {}", - entity, newVolume, slope, _systemTime.getSecondsFloat(), remainingDuration); - break; - } - else - { - ChangeAudioVolume(emitter, 0.0f); - if (emitter.isMusic) - { - _service->StopMusic(); - } - else - { - _service->StopSound(_soundCache.at(emitter.handle)); - } + // if (_systemTime < endTime) + // { + // float slope = std::get<1>(_fadeCache.at(entity)); + // float newVolume = emitter.volume + (slope * delta.getSecondsFloat()); + // ChangeAudioVolume(emitter, newVolume); + // states.PushComponentUpdateInstruction( + // entity, AudioEmitterStateComponent{AudioEmitterState::FadingOut, remainingDuration, + // emitterState.fadeExpectedVolume}); + // emitters.PushComponentUpdateInstruction( + // entity, + // AudioEmitterComponent{emitter.handle, emitter.isMusic, emitter.numberOfLoops, newVolume}); + // _logger.logDebug("Entity ID {} - EmitterState FadingOut at volume {}, slope: {}, currentTime: " + // "{}, remaining: {}", + // entity, newVolume, slope, _systemTime.getSecondsFloat(), remainingDuration); + // break; + // } + // else + // { + // ChangeAudioVolume(emitter, 0.0f); + // if (emitter.isMusic) + // { + // _mixer->StopMusic(); + // } + // else + // { + // _mixer->StopSound(_soundCache.at(emitter.handle)); + // } - emitters.PushComponentUpdateInstruction( - entity, AudioEmitterComponent{emitter.handle, emitter.isMusic, emitter.numberOfLoops, - emitterState.fadeExpectedVolume}); - states.PushComponentUpdateInstruction( - entity, AudioEmitterStateComponent{AudioEmitterState::Stopped, 0.0f, 0.0f}); - _fadeCache.erase(entity); - _logger.logDebug("Entity ID {} - EmitterState FadingOut -> Stopped", entity); - break; - } + // emitters.PushComponentUpdateInstruction( + // entity, AudioEmitterComponent{emitter.handle, emitter.isMusic, emitter.numberOfLoops, + // emitterState.fadeExpectedVolume}); + // states.PushComponentUpdateInstruction( + // entity, AudioEmitterStateComponent{AudioEmitterState::Stopped, 0.0f, 0.0f}); + // _fadeCache.erase(entity); + // _logger.logDebug("Entity ID {} - EmitterState FadingOut -> Stopped", entity); + // break; + // } - break; - } - case AudioEmitterState::ToFadeIn: - { - if (emitter.isMusic && !_service->IsMusicPlaying()) - { - _service->SetMusicVolume(0.0f); - _service->PlayMusic(_musicCache.at(emitter.handle), emitter.numberOfLoops); - } - else if (!emitter.isMusic && !_service->IsSoundPlaying(emitter.handle)) - { - auto sound = _soundCache.at(emitter.handle); - _service->SetSoundVolume(sound, 0.0f); - _service->PlaySound(sound, emitter.numberOfLoops); - } - else - { - states.PushComponentUpdateInstruction( - entity, AudioEmitterStateComponent{AudioEmitterState::Playing, 0.0f, 0.0f}); - break; - } + // break; + // } + // case AudioEmitterState::ToFadeIn: + // { + // if (emitter.isMusic && !_mixer->IsMusicPlaying()) + // { + // _mixer->SetMusicVolume(0.0f); + // _mixer->PlayMusic(_musicCache.at(emitter.handle), emitter.numberOfLoops); + // } + // else if (!emitter.isMusic && !_mixer->IsSoundPlaying(emitter.handle)) + // { + // auto sound = _soundCache.at(emitter.handle); + // _mixer->SetSoundVolume(sound, 0.0f); + // _mixer->PlaySound(sound, emitter.numberOfLoops); + // } + // else + // { + // states.PushComponentUpdateInstruction( + // entity, AudioEmitterStateComponent{AudioEmitterState::Playing, 0.0f, 0.0f}); + // break; + // } - float slope = (emitterState.fadeExpectedVolume / emitterState.fadeDuration); - Timing::Timestamp endTime = _systemTime + Timing::Timestamp::fromSeconds(emitterState.fadeDuration); - _fadeCache.insert({entity, std::make_tuple(endTime, slope)}); + // float slope = (emitterState.fadeExpectedVolume / emitterState.fadeDuration); + // Timing::Timestamp endTime = _systemTime + Timing::Timestamp::fromSeconds(emitterState.fadeDuration); + // _fadeCache.insert({entity, std::make_tuple(endTime, slope)}); - states.PushComponentUpdateInstruction( - entity, AudioEmitterStateComponent{AudioEmitterState::FadingIn, emitterState.fadeDuration, - emitterState.fadeExpectedVolume}); - emitters.PushComponentUpdateInstruction( - entity, AudioEmitterComponent{emitter.handle, emitter.isMusic, emitter.numberOfLoops, 0.0f}); - _logger.logDebug("Entity ID {} - EmitterState ToFadeIn -> FadingIn", entity); - break; - } - case AudioEmitterState::FadingIn: - { - Timing::Timestamp endTime = std::get<0>(_fadeCache.at(entity)); - float remainingDuration = (endTime - _systemTime).getSecondsFloat(); + // states.PushComponentUpdateInstruction( + // entity, AudioEmitterStateComponent{AudioEmitterState::FadingIn, emitterState.fadeDuration, + // emitterState.fadeExpectedVolume}); + // emitters.PushComponentUpdateInstruction( + // entity, AudioEmitterComponent{emitter.handle, emitter.isMusic, emitter.numberOfLoops, 0.0f}); + // _logger.logDebug("Entity ID {} - EmitterState ToFadeIn -> FadingIn", entity); + // break; + // } + // case AudioEmitterState::FadingIn: + // { + // Timing::Timestamp endTime = std::get<0>(_fadeCache.at(entity)); + // float remainingDuration = (endTime - _systemTime).getSecondsFloat(); - if (_systemTime < endTime) - { - float slope = std::get<1>(_fadeCache.at(entity)); - float newVolume = emitter.volume + (slope * delta.getSecondsFloat()); - ChangeAudioVolume(emitter, newVolume); - states.PushComponentUpdateInstruction( - entity, AudioEmitterStateComponent{AudioEmitterState::FadingIn, remainingDuration, - emitterState.fadeExpectedVolume}); - emitters.PushComponentUpdateInstruction( - entity, - AudioEmitterComponent{emitter.handle, emitter.isMusic, emitter.numberOfLoops, newVolume}); - _logger.logDebug("Entity ID {} - EmitterState FadingIn at volume {}, slope: {}, currentTime: " - "{}, remaining: {}", - entity, newVolume, slope, _systemTime.getSecondsFloat(), remainingDuration); - break; - } - else - { - if (emitter.volume < emitterState.fadeExpectedVolume) - { - ChangeAudioVolume(emitter, emitterState.fadeExpectedVolume); - } - emitters.PushComponentUpdateInstruction( - entity, AudioEmitterComponent{emitter.handle, emitter.isMusic, emitter.numberOfLoops, - emitterState.fadeExpectedVolume}); - states.PushComponentUpdateInstruction( - entity, AudioEmitterStateComponent{AudioEmitterState::Playing, 0.0f, 0.0f}); - _fadeCache.erase(entity); - _logger.logDebug("Entity ID {} - EmitterState FadingIn -> Playing", entity); - break; - } - } + // if (_systemTime < endTime) + // { + // float slope = std::get<1>(_fadeCache.at(entity)); + // float newVolume = emitter.volume + (slope * delta.getSecondsFloat()); + // ChangeAudioVolume(emitter, newVolume); + // states.PushComponentUpdateInstruction( + // entity, AudioEmitterStateComponent{AudioEmitterState::FadingIn, remainingDuration, + // emitterState.fadeExpectedVolume}); + // emitters.PushComponentUpdateInstruction( + // entity, + // AudioEmitterComponent{emitter.handle, emitter.isMusic, emitter.numberOfLoops, newVolume}); + // _logger.logDebug("Entity ID {} - EmitterState FadingIn at volume {}, slope: {}, currentTime: " + // "{}, remaining: {}", + // entity, newVolume, slope, _systemTime.getSecondsFloat(), remainingDuration); + // break; + // } + // else + // { + // if (emitter.volume < emitterState.fadeExpectedVolume) + // { + // ChangeAudioVolume(emitter, emitterState.fadeExpectedVolume); + // } + // emitters.PushComponentUpdateInstruction( + // entity, AudioEmitterComponent{emitter.handle, emitter.isMusic, emitter.numberOfLoops, + // emitterState.fadeExpectedVolume}); + // states.PushComponentUpdateInstruction( + // entity, AudioEmitterStateComponent{AudioEmitterState::Playing, 0.0f, 0.0f}); + // _fadeCache.erase(entity); + // _logger.logDebug("Entity ID {} - EmitterState FadingIn -> Playing", entity); + // break; + // } + // } case AudioEmitterState::Playing: { - float currentVolume = 0; - if (emitter.isMusic) + auto soundContext = _mixer->GetSourceContext(emitter.handle); + if (soundContext.Volume != emitter.volume) { - currentVolume = _service->GetMusicVolume(); - if (currentVolume != emitter.volume) - { - _service->SetMusicVolume(emitter.volume); - _logger.logDebug("Entity ID {} - Emitter Volume {} -> {}", entity, currentVolume, - emitter.volume); - } - if (!_service->IsMusicPlaying()) - { - states.PushComponentUpdateInstruction( - entity, AudioEmitterStateComponent{AudioEmitterState::ToStop, 0.0f, 0.0f}); - } + _mixer->SetSourceVolume(emitter.handle, emitter.volume); + _logger.logDebug("Entity ID {} - Emitter Volume {} -> {}", entity, soundContext.Volume, + emitter.volume); } - else + if (_mixer->GetSourceState(emitter.handle) != NovelRT::Audio::AudioSourceState::SOURCE_PLAYING) { - currentVolume = _service->GetSoundVolume(_soundCache.at(emitter.handle)); - if (currentVolume != emitter.volume) - { - _service->SetSoundVolume(_soundCache.at(emitter.handle), emitter.volume); - _logger.logDebug("Entity ID {} - Emitter Volume {} -> {}", entity, currentVolume, - emitter.volume); - } - if (!_service->IsSoundPlaying(_soundCache.at(emitter.handle))) - { - states.PushComponentUpdateInstruction( - entity, AudioEmitterStateComponent{AudioEmitterState::ToStop, 0.0f, 0.0f}); - } + states.PushComponentUpdateInstruction( + entity, AudioEmitterStateComponent{AudioEmitterState::ToStop, 0.0f, 0.0f}); } break; } @@ -265,63 +224,41 @@ namespace NovelRT::Ecs::Audio } } - _service->CheckSources(); + //_mixer->CheckSources(); } - uint32_t AudioSystem::CreateAudio(std::string fileName, bool isMusic) + uint32_t AudioSystem::RegisterSound(std::string fileName) { if (_counter == UINT32_MAX) { - // add logging here return 0U; } - // 0 is not valid here since we're working with only positive numbers... not that anyone should be loading - // thousands of sounds. - uint32_t value = 0U; - if (isMusic) - { - auto asset = _resourceManagerPluginProvider->GetResourceLoader()->LoadAudioFrameData(fileName); - auto handle = _service->LoadMusic(asset.processedAudioFrames, asset.channelCount, asset.sampleRate); - if (_service->IsLoaded(handle)) - { - _musicCache.insert({_counter, handle}); - value = _counter; - _counter++; - } - } - else - { - auto asset = _resourceManagerPluginProvider->GetResourceLoader()->LoadAudioFrameData(fileName); - auto handle = _service->LoadSound(asset.processedAudioFrames, asset.channelCount, asset.sampleRate); - if (_service->IsLoaded(handle)) - { - _soundCache.insert({_counter, handle}); - value = _counter; - _counter++; - } - } - - return value; + auto asset = _resourceManagerPluginProvider->GetResourceLoader()->LoadAudioFrameData(fileName); + auto handle = _mixer->SubmitAudioBuffer(asset.processedAudioFrames); + // if (_mixer->IsLoaded(handle)) + // { + // _soundCache.insert({_counter, handle}); + // value = _counter; + _counter++; + // } + return handle; } - void AudioSystem::ChangeAudioVolume(AudioEmitterComponent emitter, float desiredVolume) - { - if (emitter.isMusic) - { - _service->SetMusicVolume(desiredVolume); - } - else - { - _service->SetSoundVolume(_soundCache.at(emitter.handle), desiredVolume); - } - } + // void AudioSystem::ChangeAudioVolume(AudioEmitterComponent emitter, float desiredVolume) + // { + // if (emitter.isMusic) + // { + // _mixer->SetMusicVolume(desiredVolume); + // } + // else + // { + // _mixer->SetSoundVolume(_soundCache.at(emitter.handle), desiredVolume); + // } + // } AudioSystem::~AudioSystem() noexcept { unused(_soundCache.empty()); - unused(_musicCache.empty()); - unused(_fadeCache.empty()); - _service->TearDown(); } } From bc8222d61429192016b75c5241585f3ed21fa34a Mon Sep 17 00:00:00 2001 From: Ken Johnson Date: Sat, 17 Feb 2024 22:04:49 -0500 Subject: [PATCH 16/40] OpenAL working again with proper shutdown and playback Kind of retrofitted to current ECS setup? but the point was to make it playback using OAL first and in a separate static lib. --- audio/AudioMixer.cpp | 6 +- audio/OpenAL/OpenALAudioProvider.cpp | 31 ++- audio/include/NovelRT/Audio/AudioMixer.hpp | 2 +- .../NovelRT/Audio/AudioSourceContext.hpp | 2 + .../include/NovelRT/Audio/IAudioProvider.hpp | 2 +- .../Audio/OpenAL/OpenALAudioProvider.hpp | 5 +- include/NovelRT/Ecs/Audio/AudioSystem.h | 3 +- src/NovelRT/Ecs/Audio/AudioSystem.cpp | 252 ++++++++---------- 8 files changed, 155 insertions(+), 148 deletions(-) diff --git a/audio/AudioMixer.cpp b/audio/AudioMixer.cpp index 55020326a..6c36a467a 100644 --- a/audio/AudioMixer.cpp +++ b/audio/AudioMixer.cpp @@ -13,9 +13,11 @@ namespace NovelRT::Audio _audioProvider = std::make_unique(); } - uint32_t AudioMixer::SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span& buffer) + uint32_t AudioMixer::SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, int32_t channelCount, int32_t originalSampleRate) { - auto newContext = AudioSourceContext(); + auto newContext = AudioSourceContext{}; + newContext.Channels = channelCount; + newContext.SampleRate = originalSampleRate; uint32_t sourceId = _audioProvider->SubmitAudioBuffer(buffer, newContext); _sourceContextCache.emplace(sourceId, newContext); return sourceId; diff --git a/audio/OpenAL/OpenALAudioProvider.cpp b/audio/OpenAL/OpenALAudioProvider.cpp index 5bf3661e5..48f9cf913 100644 --- a/audio/OpenAL/OpenALAudioProvider.cpp +++ b/audio/OpenAL/OpenALAudioProvider.cpp @@ -29,11 +29,12 @@ namespace NovelRT::Audio::OpenAL void OpenALAudioProvider::Dispose() { - alSourceStopv(static_cast(_sources.size()), _sources.data()); - alDeleteSources(static_cast(_sources.size()), _sources.data()); + alSourceStopv(static_cast(_sources.size()), reinterpret_cast(_sources.data())); + alDeleteSources(static_cast(_sources.size()), reinterpret_cast(_sources.data())); _sources.clear(); - alDeleteBuffers(static_cast(_buffers.size()), _buffers.data()); + alDeleteBuffers(static_cast(_buffers.size()), reinterpret_cast(_buffers.data())); _buffers.clear(); + alcMakeContextCurrent(NULL); alcDestroyContext(_context); alcCloseDevice(_device); } @@ -45,6 +46,7 @@ namespace NovelRT::Audio::OpenAL alSourcef(source, AL_GAIN, context.Volume); alSourcef(source, AL_PITCH, context.Pitch); alSourcei(source, AL_LOOPING, static_cast(context.Loop)); + _sources.emplace_back(static_cast(source)); return source; } @@ -68,7 +70,7 @@ namespace NovelRT::Audio::OpenAL alSourcePause(sourceId); } - std::string OpenALAudioProvider::GetALError() + void OpenALAudioProvider::GetALError() { auto err = alGetError(); switch (err) @@ -100,11 +102,12 @@ namespace NovelRT::Audio::OpenAL } } - uint32_t OpenALAudioProvider::SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span& buffer, AudioSourceContext& context) + uint32_t OpenALAudioProvider::SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, AudioSourceContext& context) { ALuint alBuffer; alGenBuffers(1, &alBuffer); - alBufferData(alBuffer, AL_FORMAT_STEREO16, buffer.data(), static_cast(buffer.size() * sizeof(int16_t)), 44100); + alBufferData(alBuffer, DetermineChannelFormat(context.Channels), buffer.data(), static_cast(buffer.size() * sizeof(int16_t)), context.SampleRate); + _buffers.emplace_back(static_cast(alBuffer)); uint32_t sourceId = OpenSource(context); alSourcei(sourceId, AL_BUFFER, alBuffer); return sourceId; @@ -145,4 +148,20 @@ namespace NovelRT::Audio::OpenAL return ConvertToAudioSourceState(state); } + ALenum OpenALAudioProvider::DetermineChannelFormat(int32_t numberOfChannels) + { + switch(numberOfChannels) + { + case 1: + return AL_FORMAT_MONO16; + case 5: + return AL_FORMAT_51CHN16; + case 7: + return AL_FORMAT_71CHN16; + case 2: + default: + return AL_FORMAT_STEREO16; + } + } + } diff --git a/audio/include/NovelRT/Audio/AudioMixer.hpp b/audio/include/NovelRT/Audio/AudioMixer.hpp index 5f7a6c101..fcc3b298d 100644 --- a/audio/include/NovelRT/Audio/AudioMixer.hpp +++ b/audio/include/NovelRT/Audio/AudioMixer.hpp @@ -21,7 +21,7 @@ namespace NovelRT::Audio public: void Initialise(); - uint32_t SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span& buffer); + uint32_t SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, int32_t channelCount, int32_t originalSampleRate); void PlaySource(uint32_t id); void StopSource(uint32_t id); void PauseSource(uint32_t id); diff --git a/audio/include/NovelRT/Audio/AudioSourceContext.hpp b/audio/include/NovelRT/Audio/AudioSourceContext.hpp index 690399cd2..4fd275a93 100644 --- a/audio/include/NovelRT/Audio/AudioSourceContext.hpp +++ b/audio/include/NovelRT/Audio/AudioSourceContext.hpp @@ -12,5 +12,7 @@ namespace NovelRT::Audio float Volume = 0.75f; float Pitch = 1.0f; bool Loop = false; + int32_t Channels = 2; + int32_t SampleRate = 44100; }; } diff --git a/audio/include/NovelRT/Audio/IAudioProvider.hpp b/audio/include/NovelRT/Audio/IAudioProvider.hpp index bcfdde785..75fd5072a 100644 --- a/audio/include/NovelRT/Audio/IAudioProvider.hpp +++ b/audio/include/NovelRT/Audio/IAudioProvider.hpp @@ -15,7 +15,7 @@ namespace NovelRT::Audio virtual uint32_t OpenSource(AudioSourceContext& context) = 0; public: - virtual uint32_t SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span& buffer, AudioSourceContext& context) = 0; + virtual uint32_t SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, AudioSourceContext& context) = 0; virtual void PlaySource(uint32_t sourceId) = 0; virtual void StopSource(uint32_t sourceId) = 0; virtual void PauseSource(uint32_t sourceId) = 0; diff --git a/audio/include/NovelRT/Audio/OpenAL/OpenALAudioProvider.hpp b/audio/include/NovelRT/Audio/OpenAL/OpenALAudioProvider.hpp index e73fb6d67..57ddd6363 100644 --- a/audio/include/NovelRT/Audio/OpenAL/OpenALAudioProvider.hpp +++ b/audio/include/NovelRT/Audio/OpenAL/OpenALAudioProvider.hpp @@ -18,8 +18,9 @@ namespace NovelRT::Audio::OpenAL std::vector _sources; std::vector _buffers; - std::string GetALError(); + void GetALError(); AudioSourceState ConvertToAudioSourceState(ALenum oalSourceState); + ALenum DetermineChannelFormat(int32_t numberOfChannels); protected: void Dispose() final; @@ -31,7 +32,7 @@ namespace NovelRT::Audio::OpenAL void StopSource(uint32_t sourceId) final; void PauseSource(uint32_t sourceId) final; void SetSourceProperties(uint32_t sourceId, AudioSourceContext& context) final; - uint32_t SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span& buffer, AudioSourceContext& context) final; + uint32_t SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, AudioSourceContext& context) final; AudioSourceState GetSourceState(uint32_t id) final; ~OpenALAudioProvider() final; diff --git a/include/NovelRT/Ecs/Audio/AudioSystem.h b/include/NovelRT/Ecs/Audio/AudioSystem.h index 2b84a5476..cb7ab9e43 100644 --- a/include/NovelRT/Ecs/Audio/AudioSystem.h +++ b/include/NovelRT/Ecs/Audio/AudioSystem.h @@ -15,7 +15,8 @@ namespace NovelRT::Ecs::Audio private: uint32_t _counter; LoggingService _logger; - std::vector _soundCache; + std::map _soundCache; + std::map> _fadeCache; std::shared_ptr _mixer; NovelRT::Timing::Timestamp _systemTime; std::shared_ptr _resourceManagerPluginProvider; diff --git a/src/NovelRT/Ecs/Audio/AudioSystem.cpp b/src/NovelRT/Ecs/Audio/AudioSystem.cpp index 2111e6f57..156489f13 100644 --- a/src/NovelRT/Ecs/Audio/AudioSystem.cpp +++ b/src/NovelRT/Ecs/Audio/AudioSystem.cpp @@ -10,7 +10,8 @@ namespace NovelRT::Ecs::Audio : _counter(1), _logger(Utilities::Misc::CONSOLE_LOG_AUDIO), _mixer(std::make_shared()), - _soundCache(std::vector()), + _soundCache(std::map()), + _fadeCache(std::map>()), _systemTime(Timing::Timestamp::zero()), _resourceManagerPluginProvider(std::move(resourceManagerPluginProvider)) { @@ -60,145 +61,127 @@ namespace NovelRT::Ecs::Audio entity, AudioEmitterStateComponent{AudioEmitterState::Playing, emitterState.fadeDuration}); break; } - // case AudioEmitterState::ToFadeOut: - // { - // if (emitter.isMusic && !_mixer->IsMusicPlaying()) - // { - // states.PushComponentUpdateInstruction( - // entity, AudioEmitterStateComponent{AudioEmitterState::Stopped, 0.0f, 0.0f}); - // break; - // } - // else if (!emitter.isMusic && !_mixer->IsSoundPlaying(_soundCache.at(emitter.handle))) - // { - // states.PushComponentUpdateInstruction( - // entity, AudioEmitterStateComponent{AudioEmitterState::Stopped, 0.0f, 0.0f}); - // break; - // } + case AudioEmitterState::ToFadeOut: + { + if (_mixer->GetSourceState(emitter.handle) == NovelRT::Audio::AudioSourceState::SOURCE_STOPPED) + { + states.PushComponentUpdateInstruction( + entity, AudioEmitterStateComponent{AudioEmitterState::Stopped, 0.0f, 0.0f}); + break; + } - // float slope = -(emitter.volume / emitterState.fadeDuration); - // Timing::Timestamp endTime = _systemTime + Timing::Timestamp::fromSeconds(emitterState.fadeDuration); - // _fadeCache.insert({entity, std::make_tuple(endTime, slope)}); + float slope = -(emitter.volume / emitterState.fadeDuration); + Timing::Timestamp endTime = _systemTime + Timing::Timestamp::fromSeconds(emitterState.fadeDuration); + _fadeCache.insert({entity, std::make_tuple(endTime, slope)}); - // states.PushComponentUpdateInstruction( - // entity, - // AudioEmitterStateComponent{AudioEmitterState::FadingOut, emitterState.fadeDuration, 0.0f}); - // _logger.logDebug("Entity ID {} - EmitterState ToFadeOut -> FadingOut", entity); - // break; - // } - // case AudioEmitterState::FadingOut: - // { - // Timing::Timestamp endTime = std::get<0>(_fadeCache.at(entity)); - // float remainingDuration = (endTime - _systemTime).getSecondsFloat(); + states.PushComponentUpdateInstruction( + entity, + AudioEmitterStateComponent{AudioEmitterState::FadingOut, emitterState.fadeDuration, 0.0f}); + _logger.logDebug("Entity ID {} - EmitterState ToFadeOut -> FadingOut", entity); + break; + } + case AudioEmitterState::FadingOut: + { + Timing::Timestamp endTime = std::get<0>(_fadeCache.at(entity)); + float remainingDuration = (endTime - _systemTime).getSecondsFloat(); - // if (_systemTime < endTime) - // { - // float slope = std::get<1>(_fadeCache.at(entity)); - // float newVolume = emitter.volume + (slope * delta.getSecondsFloat()); - // ChangeAudioVolume(emitter, newVolume); - // states.PushComponentUpdateInstruction( - // entity, AudioEmitterStateComponent{AudioEmitterState::FadingOut, remainingDuration, - // emitterState.fadeExpectedVolume}); - // emitters.PushComponentUpdateInstruction( - // entity, - // AudioEmitterComponent{emitter.handle, emitter.isMusic, emitter.numberOfLoops, newVolume}); - // _logger.logDebug("Entity ID {} - EmitterState FadingOut at volume {}, slope: {}, currentTime: " - // "{}, remaining: {}", - // entity, newVolume, slope, _systemTime.getSecondsFloat(), remainingDuration); - // break; - // } - // else - // { - // ChangeAudioVolume(emitter, 0.0f); - // if (emitter.isMusic) - // { - // _mixer->StopMusic(); - // } - // else - // { - // _mixer->StopSound(_soundCache.at(emitter.handle)); - // } + if (_systemTime < endTime) + { + float slope = std::get<1>(_fadeCache.at(entity)); + float newVolume = emitter.volume + (slope * delta.getSecondsFloat()); + _mixer->SetSourceVolume(emitter.handle, newVolume); - // emitters.PushComponentUpdateInstruction( - // entity, AudioEmitterComponent{emitter.handle, emitter.isMusic, emitter.numberOfLoops, - // emitterState.fadeExpectedVolume}); - // states.PushComponentUpdateInstruction( - // entity, AudioEmitterStateComponent{AudioEmitterState::Stopped, 0.0f, 0.0f}); - // _fadeCache.erase(entity); - // _logger.logDebug("Entity ID {} - EmitterState FadingOut -> Stopped", entity); - // break; - // } + states.PushComponentUpdateInstruction( + entity, AudioEmitterStateComponent{AudioEmitterState::FadingOut, remainingDuration, + emitterState.fadeExpectedVolume}); + emitters.PushComponentUpdateInstruction( + entity, + AudioEmitterComponent{emitter.handle, emitter.isMusic, emitter.numberOfLoops, newVolume}); + _logger.logDebug("Entity ID {} - EmitterState FadingOut at volume {}, slope: {}, currentTime: " + "{}, remaining: {}", + entity, newVolume, slope, _systemTime.getSecondsFloat(), remainingDuration); + break; + } + else + { + _mixer->SetSourceVolume(emitter.handle, 0.0f); + _mixer->StopSource(emitter.handle); - // break; - // } - // case AudioEmitterState::ToFadeIn: - // { - // if (emitter.isMusic && !_mixer->IsMusicPlaying()) - // { - // _mixer->SetMusicVolume(0.0f); - // _mixer->PlayMusic(_musicCache.at(emitter.handle), emitter.numberOfLoops); - // } - // else if (!emitter.isMusic && !_mixer->IsSoundPlaying(emitter.handle)) - // { - // auto sound = _soundCache.at(emitter.handle); - // _mixer->SetSoundVolume(sound, 0.0f); - // _mixer->PlaySound(sound, emitter.numberOfLoops); - // } - // else - // { - // states.PushComponentUpdateInstruction( - // entity, AudioEmitterStateComponent{AudioEmitterState::Playing, 0.0f, 0.0f}); - // break; - // } + emitters.PushComponentUpdateInstruction( + entity, AudioEmitterComponent{emitter.handle, emitter.isMusic, emitter.numberOfLoops, + emitterState.fadeExpectedVolume}); + states.PushComponentUpdateInstruction( + entity, AudioEmitterStateComponent{AudioEmitterState::Stopped, 0.0f, 0.0f}); + _fadeCache.erase(entity); + _logger.logDebug("Entity ID {} - EmitterState FadingOut -> Stopped", entity); + break; + } - // float slope = (emitterState.fadeExpectedVolume / emitterState.fadeDuration); - // Timing::Timestamp endTime = _systemTime + Timing::Timestamp::fromSeconds(emitterState.fadeDuration); - // _fadeCache.insert({entity, std::make_tuple(endTime, slope)}); + break; + } + case AudioEmitterState::ToFadeIn: + { + if (_mixer->GetSourceState(emitter.handle) == NovelRT::Audio::AudioSourceState::SOURCE_STOPPED) + { + _mixer->SetSourceVolume(emitter.handle, 0.0f); + _mixer->PlaySource(emitter.handle); + } + else + { + states.PushComponentUpdateInstruction( + entity, AudioEmitterStateComponent{AudioEmitterState::Playing, 0.0f, 0.0f}); + break; + } + + float slope = (emitterState.fadeExpectedVolume / emitterState.fadeDuration); + Timing::Timestamp endTime = _systemTime + Timing::Timestamp::fromSeconds(emitterState.fadeDuration); + _fadeCache.insert({entity, std::make_tuple(endTime, slope)}); - // states.PushComponentUpdateInstruction( - // entity, AudioEmitterStateComponent{AudioEmitterState::FadingIn, emitterState.fadeDuration, - // emitterState.fadeExpectedVolume}); - // emitters.PushComponentUpdateInstruction( - // entity, AudioEmitterComponent{emitter.handle, emitter.isMusic, emitter.numberOfLoops, 0.0f}); - // _logger.logDebug("Entity ID {} - EmitterState ToFadeIn -> FadingIn", entity); - // break; - // } - // case AudioEmitterState::FadingIn: - // { - // Timing::Timestamp endTime = std::get<0>(_fadeCache.at(entity)); - // float remainingDuration = (endTime - _systemTime).getSecondsFloat(); + states.PushComponentUpdateInstruction( + entity, AudioEmitterStateComponent{AudioEmitterState::FadingIn, emitterState.fadeDuration, + emitterState.fadeExpectedVolume}); + emitters.PushComponentUpdateInstruction( + entity, AudioEmitterComponent{emitter.handle, emitter.isMusic, emitter.numberOfLoops, 0.0f}); + _logger.logDebug("Entity ID {} - EmitterState ToFadeIn -> FadingIn", entity); + break; + } + case AudioEmitterState::FadingIn: + { + Timing::Timestamp endTime = std::get<0>(_fadeCache.at(entity)); + float remainingDuration = (endTime - _systemTime).getSecondsFloat(); - // if (_systemTime < endTime) - // { - // float slope = std::get<1>(_fadeCache.at(entity)); - // float newVolume = emitter.volume + (slope * delta.getSecondsFloat()); - // ChangeAudioVolume(emitter, newVolume); - // states.PushComponentUpdateInstruction( - // entity, AudioEmitterStateComponent{AudioEmitterState::FadingIn, remainingDuration, - // emitterState.fadeExpectedVolume}); - // emitters.PushComponentUpdateInstruction( - // entity, - // AudioEmitterComponent{emitter.handle, emitter.isMusic, emitter.numberOfLoops, newVolume}); - // _logger.logDebug("Entity ID {} - EmitterState FadingIn at volume {}, slope: {}, currentTime: " - // "{}, remaining: {}", - // entity, newVolume, slope, _systemTime.getSecondsFloat(), remainingDuration); - // break; - // } - // else - // { - // if (emitter.volume < emitterState.fadeExpectedVolume) - // { - // ChangeAudioVolume(emitter, emitterState.fadeExpectedVolume); - // } - // emitters.PushComponentUpdateInstruction( - // entity, AudioEmitterComponent{emitter.handle, emitter.isMusic, emitter.numberOfLoops, - // emitterState.fadeExpectedVolume}); - // states.PushComponentUpdateInstruction( - // entity, AudioEmitterStateComponent{AudioEmitterState::Playing, 0.0f, 0.0f}); - // _fadeCache.erase(entity); - // _logger.logDebug("Entity ID {} - EmitterState FadingIn -> Playing", entity); - // break; - // } - // } + if (_systemTime < endTime) + { + float slope = std::get<1>(_fadeCache.at(entity)); + float newVolume = emitter.volume + (slope * delta.getSecondsFloat()); + _mixer->SetSourceVolume(emitter.handle, newVolume); + states.PushComponentUpdateInstruction( + entity, AudioEmitterStateComponent{AudioEmitterState::FadingIn, remainingDuration, + emitterState.fadeExpectedVolume}); + emitters.PushComponentUpdateInstruction( + entity, + AudioEmitterComponent{emitter.handle, emitter.isMusic, emitter.numberOfLoops, newVolume}); + _logger.logDebug("Entity ID {} - EmitterState FadingIn at volume {}, slope: {}, currentTime: " + "{}, remaining: {}", + entity, newVolume, slope, _systemTime.getSecondsFloat(), remainingDuration); + break; + } + else + { + if (emitter.volume < emitterState.fadeExpectedVolume) + { + _mixer->SetSourceVolume(emitter.handle, emitterState.fadeExpectedVolume); + } + emitters.PushComponentUpdateInstruction( + entity, AudioEmitterComponent{emitter.handle, emitter.isMusic, emitter.numberOfLoops, + emitterState.fadeExpectedVolume}); + states.PushComponentUpdateInstruction( + entity, AudioEmitterStateComponent{AudioEmitterState::Playing, 0.0f, 0.0f}); + _fadeCache.erase(entity); + _logger.logDebug("Entity ID {} - EmitterState FadingIn -> Playing", entity); + break; + } + } case AudioEmitterState::Playing: { auto soundContext = _mixer->GetSourceContext(emitter.handle); @@ -223,8 +206,6 @@ namespace NovelRT::Ecs::Audio } } } - - //_mixer->CheckSources(); } uint32_t AudioSystem::RegisterSound(std::string fileName) @@ -235,7 +216,8 @@ namespace NovelRT::Ecs::Audio } auto asset = _resourceManagerPluginProvider->GetResourceLoader()->LoadAudioFrameData(fileName); - auto handle = _mixer->SubmitAudioBuffer(asset.processedAudioFrames); + auto handle = _mixer->SubmitAudioBuffer(NovelRT::Utilities::Misc::Span(asset.processedAudioFrames.data(), asset.processedAudioFrames.size()), asset.channelCount, asset.sampleRate); + _soundCache.emplace(handle, asset); // if (_mixer->IsLoaded(handle)) // { // _soundCache.insert({_counter, handle}); From cd5c187737824b354817b1c63c97db3036d92577 Mon Sep 17 00:00:00 2001 From: Ken Johnson Date: Sun, 18 Feb 2024 00:17:58 -0500 Subject: [PATCH 17/40] Update OpenAL and hook logging --- CMakeLists.txt | 3 +- README.md | 5 +- audio/OpenAL/OpenALAudioProvider.cpp | 90 +++++++++++++++++-- .../Audio/OpenAL/OpenALAudioProvider.hpp | 6 +- thirdparty/CMakeLists.txt | 4 +- thirdparty/OpenAL/CMakeLists.txt | 2 +- 6 files changed, 97 insertions(+), 13 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 80901d84e..ef7238c85 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -64,10 +64,11 @@ option(NOVELRT_BUILD_DEPS_WITH_MAX_CPU "Use all available CPU processing power w # set(NOVELRT_DOXYGEN_VERSION "1.8.17" CACHE STRING "Doxygen version") set(NOVELRT_FLAC_VERSION "1.3.4" CACHE STRING "FLAC version") +set(NOVELRT_FMT_VERSION "10.2.1" CACHE STRING "FMT version") set(NOVELRT_GLFW_VERSION "3.3.7" CACHE STRING "GLFW3 version") set(NOVELRT_GSL_VERSION "4.0.0" CACHE STRING "Microsoft.GSL version") set(NOVELRT_ONETBB_VERSION "2021.5.0" CACHE STRING "OneTBB version") -set(NOVELRT_OPENAL_VERSION "1.21.1" CACHE STRING "OpenAL version") +set(NOVELRT_OPENAL_VERSION "1.23.1" CACHE STRING "OpenAL version") set(NOVELRT_OGG_VERSION "1.3.5" CACHE STRING "Ogg version") set(NOVELRT_OPUS_VERSION "1.3.1" CACHE STRING "Opus version") set(NOVELRT_PNG_VERSION "1.6.35" CACHE STRING "PNG version") diff --git a/README.md b/README.md index 5a0cd9d06..8dcfff720 100644 --- a/README.md +++ b/README.md @@ -42,11 +42,12 @@ The dependencies that are handled by CMake that do not need to be manually insta - GLFW 3.3.7 - glm 0.9.9.9 - gtest/gmock 1.11.0 +- fmt 10.2.1 - libpng 1.6.35 - libsndfile 1.1.0 - Microsoft GSL 4.0.0 - OneTBB 2021.5.0 -- OpenAL 1.21.1 +- OpenAL 1.23.1 - spdlog 1.13.0 ### Build instructions @@ -144,7 +145,7 @@ cmake .. -DCMAKE_APPLE_SILICON_PROCESSOR="arm64" If Vulkan SDK is not installed in a system path and the `setup-env.sh` file did not properly add the required environment variables, you can specify the `VULKAN_SDK` environment variable to your local Vulkan SDK location as such: ``` -VULKAN_SDK=/Users/youruser/Vulkan SDK/1.3.231.1/macOS cmake .. +VULKAN_SDK=/Users/youruser/Vulkan SDK/1.3.231.1/macOS cmake .. ``` Please ensure that the path includes the macOS folder, otherwise finding the proper libraries will fail. diff --git a/audio/OpenAL/OpenALAudioProvider.cpp b/audio/OpenAL/OpenALAudioProvider.cpp index 48f9cf913..be00e4c8b 100644 --- a/audio/OpenAL/OpenALAudioProvider.cpp +++ b/audio/OpenAL/OpenALAudioProvider.cpp @@ -2,13 +2,30 @@ // for more information. #include #include +#include namespace NovelRT::Audio::OpenAL { - OpenALAudioProvider::OpenALAudioProvider() + typedef void (ALC_APIENTRY*LoggingCallback)(void*, char, const char*, int) noexcept; + typedef void (ALC_APIENTRY*CallbackProvider)(LoggingCallback, void*) noexcept; + + OpenALAudioProvider::OpenALAudioProvider(): + _buffers(std::vector()), + _sources(std::vector()), + _logger(spdlog::stdout_color_mt("OpenAL")) { - _buffers = std::vector(); - _sources = std::vector(); + //Logger init + _logger->set_level(spdlog::level::debug); + CallbackProvider setLogCallback = reinterpret_cast(alcGetProcAddress(nullptr, "alsoft_set_log_callback")); + if (setLogCallback != nullptr) + { + setLogCallback(static_cast([](void* ptr, char l, const char* m, int) noexcept + { + auto provider = reinterpret_cast(ptr); + provider->LogOpenALMessages(l, m); + }), this); + } + //Device and Context Init _device = alcOpenDevice(nullptr); @@ -19,6 +36,12 @@ namespace NovelRT::Audio::OpenAL "OpenAL failed to create an audio device!", error); } _context = alcCreateContext(_device, nullptr); + if(!_context) + { + std::string error = GetALError(); + throw Exceptions::InitialisationFailureException( + "OpenAL failed to create and attach a proper context!", error); + } alcMakeContextCurrent(_context); } @@ -29,23 +52,35 @@ namespace NovelRT::Audio::OpenAL void OpenALAudioProvider::Dispose() { + alGetError(); alSourceStopv(static_cast(_sources.size()), reinterpret_cast(_sources.data())); + GetALError(); alDeleteSources(static_cast(_sources.size()), reinterpret_cast(_sources.data())); + GetALError(); _sources.clear(); alDeleteBuffers(static_cast(_buffers.size()), reinterpret_cast(_buffers.data())); + GetALError(); _buffers.clear(); alcMakeContextCurrent(NULL); + GetALError(); alcDestroyContext(_context); + GetALError(); alcCloseDevice(_device); + GetALError(); } uint32_t OpenALAudioProvider::OpenSource(AudioSourceContext& context) { uint32_t source = 0; + alGetError(); alGenSources(1, &source); + GetALError(); alSourcef(source, AL_GAIN, context.Volume); + GetALError(); alSourcef(source, AL_PITCH, context.Pitch); + GetALError(); alSourcei(source, AL_LOOPING, static_cast(context.Loop)); + GetALError(); _sources.emplace_back(static_cast(source)); return source; } @@ -57,47 +92,63 @@ namespace NovelRT::Audio::OpenAL void OpenALAudioProvider::PlaySource(uint32_t sourceId) { + alGetError(); alSourcePlay(sourceId); + GetALError(); } void OpenALAudioProvider::StopSource(uint32_t sourceId) { + alGetError(); alSourceStop(sourceId); + GetALError(); } void OpenALAudioProvider::PauseSource(uint32_t sourceId) { + alGetError(); alSourcePause(sourceId); + GetALError(); } - void OpenALAudioProvider::GetALError() + std::string OpenALAudioProvider::GetALError() { auto err = alGetError(); switch (err) { case AL_INVALID_NAME: { + _logger->error("A bad ID or name was passed to the OpenAL function."); return std::string("A bad ID or name was passed to the OpenAL function."); } case AL_INVALID_ENUM: { + _logger->error("An invalid enum was passed to an OpenAL function."); return std::string("An invalid enum was passed to an OpenAL function."); } case AL_INVALID_VALUE: { + _logger->error("An invalid value was passed to an OpenAL function."); return std::string("An invalid value was passed to an OpenAL function."); } case AL_INVALID_OPERATION: { + _logger->error("The requested operation is not valid."); return std::string("The requested operation is not valid."); } case AL_OUT_OF_MEMORY: { + _logger->error("The requested operation resulted in OpenAL running out of memory."); return std::string("The requested operation resulted in OpenAL running out of memory."); } + case AL_NO_ERROR: + { + return std::string(); + } default: { - return std::string(""); + _logger->error("Unknown OpenAL Error - Code: {err}", err); + return std::string("Unknown OpenAL Error - Code: " + err); } } } @@ -105,19 +156,27 @@ namespace NovelRT::Audio::OpenAL uint32_t OpenALAudioProvider::SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, AudioSourceContext& context) { ALuint alBuffer; + alGetError(); alGenBuffers(1, &alBuffer); + GetALError(); alBufferData(alBuffer, DetermineChannelFormat(context.Channels), buffer.data(), static_cast(buffer.size() * sizeof(int16_t)), context.SampleRate); + GetALError(); _buffers.emplace_back(static_cast(alBuffer)); uint32_t sourceId = OpenSource(context); alSourcei(sourceId, AL_BUFFER, alBuffer); + GetALError(); return sourceId; } void OpenALAudioProvider::SetSourceProperties(uint32_t sourceId, AudioSourceContext& context) { + alGetError(); alSourcef(sourceId, AL_GAIN, context.Volume); + GetALError(); alSourcef(sourceId, AL_PITCH, context.Pitch); + GetALError(); alSourcei(sourceId, AL_LOOPING, static_cast(context.Loop)); + GetALError(); } AudioSourceState OpenALAudioProvider::ConvertToAudioSourceState(ALenum oALSourceState) @@ -144,7 +203,9 @@ namespace NovelRT::Audio::OpenAL AudioSourceState OpenALAudioProvider::GetSourceState(uint32_t sourceId) { ALenum state = 0x0; + alGetError(); alGetSourcei(sourceId, AL_SOURCE_STATE, &state); + GetALError(); return ConvertToAudioSourceState(state); } @@ -164,4 +225,23 @@ namespace NovelRT::Audio::OpenAL } } + void OpenALAudioProvider::LogOpenALMessages(char level, const char* message) + { + switch(level) + { + case 'W': + { + _logger->warn(message); + } + case 'E': + { + _logger->error(message); + } + case 'I': + default: + { + _logger->debug(message); + } + } + } } diff --git a/audio/include/NovelRT/Audio/OpenAL/OpenALAudioProvider.hpp b/audio/include/NovelRT/Audio/OpenAL/OpenALAudioProvider.hpp index 57ddd6363..61f62720a 100644 --- a/audio/include/NovelRT/Audio/OpenAL/OpenALAudioProvider.hpp +++ b/audio/include/NovelRT/Audio/OpenAL/OpenALAudioProvider.hpp @@ -6,21 +6,23 @@ #include #include #include +#include namespace NovelRT::Audio::OpenAL { class OpenALAudioProvider : public IAudioProvider { private: - ALuint _outputSource; ALCdevice* _device; ALCcontext* _context; std::vector _sources; std::vector _buffers; + std::shared_ptr _logger; - void GetALError(); + std::string GetALError(); AudioSourceState ConvertToAudioSourceState(ALenum oalSourceState); ALenum DetermineChannelFormat(int32_t numberOfChannels); + void LogOpenALMessages(char level, const char* message); protected: void Dispose() final; diff --git a/thirdparty/CMakeLists.txt b/thirdparty/CMakeLists.txt index a221e601f..edae415b1 100644 --- a/thirdparty/CMakeLists.txt +++ b/thirdparty/CMakeLists.txt @@ -56,8 +56,8 @@ thirdparty_module(Ogg OVERRIDE_FIND_PACKAGE ) thirdparty_module(OpenAL - URL https://github.com/kcat/openal-soft/archive/refs/tags/1.21.1.zip - URL_HASH SHA512=bc17d628548e59d0db3996917d207a4af0bbbf615ba3ba10ae8e99b28213e845e967a0510c8aad74e551aa66ddfb11499fe26082725af82160ac5d2db4e7e69d + URL https://github.com/kcat/openal-soft/archive/c03603b58d4cf6a25d36bca00305970bc9f163b4.zip + URL_HASH SHA512=331bf7d7ee138e3059dcdff68de153f73ba9b30dd098a0f5913d1e51d7292b8ad6f0e8642cb125bb8a757e70660a20375e5e76b70ddc59e5cbaab25676cbf9b8 OVERRIDE_FIND_PACKAGE ) thirdparty_module(Opus diff --git a/thirdparty/OpenAL/CMakeLists.txt b/thirdparty/OpenAL/CMakeLists.txt index 6edf185c6..a01bbabdf 100644 --- a/thirdparty/OpenAL/CMakeLists.txt +++ b/thirdparty/OpenAL/CMakeLists.txt @@ -27,7 +27,7 @@ target_compile_options(OpenAL $<$:-Wno-deprecated-copy> ) -target_compile_options(common +target_compile_options(alcommon PRIVATE $<$:/wd4127> $<$:/wd4834> From 26f0cf45000264c770af95095aa365a6628c1e26 Mon Sep 17 00:00:00 2001 From: Ken Johnson Date: Sun, 18 Feb 2024 09:17:51 -0500 Subject: [PATCH 18/40] Partially working XAudio2 for Windows Need to fix Ogg playback --- audio/AudioMixer.cpp | 9 +- audio/CMakeLists.txt | 11 +- audio/XAudio2/XAudio2AudioProvider.cpp | 176 ++++++++++++++++++ .../Audio/XAudio2/XAudio2AudioProvider.hpp | 45 +++++ 4 files changed, 237 insertions(+), 4 deletions(-) create mode 100644 audio/XAudio2/XAudio2AudioProvider.cpp create mode 100644 audio/include/NovelRT/Audio/XAudio2/XAudio2AudioProvider.hpp diff --git a/audio/AudioMixer.cpp b/audio/AudioMixer.cpp index 6c36a467a..762bcc0f9 100644 --- a/audio/AudioMixer.cpp +++ b/audio/AudioMixer.cpp @@ -3,14 +3,21 @@ #include //Conditional +#if defined(_WIN32) +#include +#else #include - +#endif namespace NovelRT::Audio { void AudioMixer::Initialise() { _sourceContextCache = std::map(); +#if defined(_WIN32) + _audioProvider = std::make_unique(); +#else _audioProvider = std::make_unique(); +#endif } uint32_t AudioMixer::SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, int32_t channelCount, int32_t originalSampleRate) diff --git a/audio/CMakeLists.txt b/audio/CMakeLists.txt index 1d873f634..ced6b14da 100644 --- a/audio/CMakeLists.txt +++ b/audio/CMakeLists.txt @@ -1,6 +1,8 @@ add_library(NovelRT-Audio STATIC AudioMixer.cpp - OpenAL/OpenALAudioProvider.cpp + $<$:XAudio2/XAudio2AudioProvider.cpp> + $<$:OpenAL/OpenALAudioProvider.cpp> + $<$:OpenAL/OpenALAudioProvider.cpp> ) target_sources(NovelRT-Audio @@ -11,7 +13,9 @@ target_sources(NovelRT-Audio FILES include/NovelRT/Audio/AudioMixer.hpp include/NovelRT/Audio/IAudioProvider.hpp - include/NovelRT/Audio/OpenAL/OpenALAudioProvider.hpp + $<$:include/NovelRT/Audio/XAudio2/XAudio2AudioProvider.hpp> + $<$:include/NovelRT/Audio/OpenAL/OpenALAudioProvider.hpp> + $<$:include/NovelRT/Audio/OpenAL/OpenALAudioProvider.hpp> ) set_target_properties(NovelRT-Audio @@ -49,7 +53,8 @@ target_include_directories(NovelRT-Audio PRIVATE "$:OpenAL> + $<$:OpenAL> ) install( diff --git a/audio/XAudio2/XAudio2AudioProvider.cpp b/audio/XAudio2/XAudio2AudioProvider.cpp new file mode 100644 index 000000000..91ceadc8e --- /dev/null +++ b/audio/XAudio2/XAudio2AudioProvider.cpp @@ -0,0 +1,176 @@ +// Copyright © Matt Jones and Contributors. Licensed under the MIT Licence (MIT). See LICENCE.md in the repository root +// for more information. +#include +#include +#include + +namespace NovelRT::Audio::XAudio2 +{ + + XAudio2AudioProvider::XAudio2AudioProvider(): + _device(nullptr), + _masterVoice(nullptr), + _sources(std::map()), + _sourceCounter(0), + _logger(spdlog::stdout_color_mt("XAudio2")), + _buffers(std::map()), + _bufferCounter(0) + { + //Logger init + _logger->set_level(spdlog::level::debug); + + //Device and Context Init + _hr = CoInitializeEx( nullptr, COINIT_MULTITHREADED ); + if (FAILED(_hr)) + { + throw new Exceptions::InitialisationFailureException("Failed to initialise COM!", _hr); + } + + if (FAILED(_hr = XAudio2Create( &_device, 0, XAUDIO2_DEFAULT_PROCESSOR))) + { + throw new Exceptions::InitialisationFailureException("Failed to create an instance of the XAudio2 engine!", _hr); + } + + if (FAILED(_hr = _device->CreateMasteringVoice(&_masterVoice, XAUDIO2_DEFAULT_CHANNELS, XAUDIO2_DEFAULT_SAMPLERATE))) + { + throw new Exceptions::InitialisationFailureException("Failed to create an output voice!", _hr); + } + + XAUDIO2_DEBUG_CONFIGURATION debug{}; + debug.TraceMask = XAUDIO2_LOG_ERRORS; + _device->SetDebugConfiguration(&debug); + + _logger->info("XAudio2 initialised."); + } + + XAudio2AudioProvider::~XAudio2AudioProvider() + { + Dispose(); + } + + void XAudio2AudioProvider::Dispose() + { + for(auto [id, source] : _sources) + { + source->FlushSourceBuffers(); + source->DestroyVoice(); + } + _sources.clear(); + _buffers.clear(); + _masterVoice->DestroyVoice(); + _device->StopEngine(); + } + + uint32_t XAudio2AudioProvider::OpenSource(AudioSourceContext& context) + { + uint32_t nextSource = ++_sourceCounter; + WAVEFORMATEX waveFormatContainer{}; + waveFormatContainer.wFormatTag = WAVE_FORMAT_PCM; + waveFormatContainer.nChannels = 2; + waveFormatContainer.nSamplesPerSec = static_cast(context.SampleRate); + waveFormatContainer.nAvgBytesPerSec = static_cast(context.SampleRate / 0.25); + waveFormatContainer.nBlockAlign = 4; + waveFormatContainer.wBitsPerSample = 16; + waveFormatContainer.cbSize = 0; + + + IXAudio2SourceVoice* newVoice; + if(FAILED(_hr = _device->CreateSourceVoice(&newVoice, &waveFormatContainer))) + { + _logger->error("Could not create source voice - Code: {hr}", _hr); + } + + _sources.emplace(nextSource, newVoice); + return nextSource; + } + + void XAudio2AudioProvider::PlaySource(uint32_t sourceId) + { + if(FAILED(_hr = _sources.at(sourceId)->Start(0))) + { + _logger->error("Error when attempting to play source {id} - Code: {hr}", sourceId, _hr); + } + } + + void XAudio2AudioProvider::StopSource(uint32_t sourceId) + { + if(FAILED(_hr = _sources.at(sourceId)->Stop(0))) + { + _logger->error("Error when stopping source {id} - Code: {hr}", sourceId, _hr); + } + } + + void XAudio2AudioProvider::PauseSource(uint32_t sourceId) + { + PlaySource(sourceId); + } + + uint32_t XAudio2AudioProvider::SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, AudioSourceContext& context) + { + uint32_t nextBuffer = ++_bufferCounter; + XAUDIO2_BUFFER xABuffer = + { + XAUDIO2_END_OF_STREAM, // Flags + static_cast(buffer.size()*sizeof(int16_t)), // AudioBytes + static_cast(new byte[buffer.size()*sizeof(int16_t)]) + }; + + std::memcpy((void*)(xABuffer.pAudioData), + reinterpret_cast(buffer.data()), buffer.size()*sizeof(int16_t)); + if(context.Loop) + { + xABuffer.LoopCount = XAUDIO2_LOOP_INFINITE; + } + + _buffers.emplace(nextBuffer, xABuffer); + uint32_t sourceId = OpenSource(context); + + if(FAILED(_hr = _sources.at(sourceId)->SubmitSourceBuffer(&xABuffer))) + { + _logger->error("Failed to submit buffer to source {sourceId} - Code: {hr}", sourceId, std::to_string(_hr)); + } + + return sourceId; + } + + void XAudio2AudioProvider::SetSourceProperties(uint32_t sourceId, AudioSourceContext& context) + { + //volume + if(FAILED(_hr = _sources.at(sourceId)->SetVolume(ConvertToXAudio2VolumeUnits(context.Volume)))) + { + _logger->error("Error when setting volume for source {id} - Code: {hr}", sourceId, _hr); + } + //pitch + if(FAILED(_hr = _sources.at(sourceId)->SetFrequencyRatio(context.Pitch))) + { + _logger->error("Error when setting pitch for source {id} - Code: {hr}", sourceId, _hr); + } + } + + AudioSourceState XAudio2AudioProvider::GetSourceState(uint32_t sourceId) + { + XAUDIO2_VOICE_STATE voiceState; + _sources.at(sourceId)->GetState(&voiceState, 0); + return ConvertToAudioSourceState(voiceState); + } + + float XAudio2AudioProvider::ConvertToXAudio2VolumeUnits(float inputVolume) + { + return inputVolume * (XAUDIO2_MAX_VOLUME_LEVEL - (-XAUDIO2_MAX_VOLUME_LEVEL)) + -XAUDIO2_MAX_VOLUME_LEVEL; + } + + AudioSourceState XAudio2AudioProvider::ConvertToAudioSourceState(XAUDIO2_VOICE_STATE sourceState) + { + switch(sourceState.BuffersQueued) + { + case NULL: + { + return AudioSourceState::SOURCE_STOPPED; + } + default: + { + return AudioSourceState::SOURCE_PLAYING; + } + } + } +} diff --git a/audio/include/NovelRT/Audio/XAudio2/XAudio2AudioProvider.hpp b/audio/include/NovelRT/Audio/XAudio2/XAudio2AudioProvider.hpp new file mode 100644 index 000000000..f17c94441 --- /dev/null +++ b/audio/include/NovelRT/Audio/XAudio2/XAudio2AudioProvider.hpp @@ -0,0 +1,45 @@ +// Copyright © Matt Jones and Contributors. Licensed under the MIT Licence (MIT). See LICENCE.md in the repository root +// for more information. +#pragma once + +#include +#include +#include +#include +#include + +namespace NovelRT::Audio::XAudio2 +{ + class XAudio2AudioProvider : public IAudioProvider + { + private: + IXAudio2* _device; + IXAudio2MasteringVoice* _masterVoice; + std::map _sources; + uint32_t _sourceCounter; + std::shared_ptr _logger; + uint32_t _bufferCounter; + std::map _buffers; + HRESULT _hr; + + float ConvertToXAudio2VolumeUnits(float inputVolume); + AudioSourceState ConvertToAudioSourceState(XAUDIO2_VOICE_STATE sourceState); + // ALenum DetermineChannelFormat(int32_t numberOfChannels); + // void LogOpenALMessages(char level, const char* message); + + protected: + void Dispose() final; + uint32_t OpenSource(AudioSourceContext& context) final; + + public: + XAudio2AudioProvider(); + void PlaySource(uint32_t sourceId) final; + void StopSource(uint32_t sourceId) final; + void PauseSource(uint32_t sourceId) final; + void SetSourceProperties(uint32_t sourceId, AudioSourceContext& context) final; + uint32_t SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, AudioSourceContext& context) final; + AudioSourceState GetSourceState(uint32_t id) final; + + ~XAudio2AudioProvider() final; + }; +} From 09d64f17763fe03c302d26b31bf88b524ec49b26 Mon Sep 17 00:00:00 2001 From: Ken Johnson Date: Sun, 18 Feb 2024 16:41:52 -0500 Subject: [PATCH 19/40] XAudio2 now conforming to AudioSystem --- audio/XAudio2/XAudio2AudioProvider.cpp | 49 ++++++++++----- .../Audio/XAudio2/XAudio2AudioProvider.hpp | 3 - src/NovelRT/CMakeLists.txt | 2 +- thirdparty/OpenAL/CMakeLists.txt | 61 ++++++++++--------- 4 files changed, 67 insertions(+), 48 deletions(-) diff --git a/audio/XAudio2/XAudio2AudioProvider.cpp b/audio/XAudio2/XAudio2AudioProvider.cpp index 91ceadc8e..33e78c6ba 100644 --- a/audio/XAudio2/XAudio2AudioProvider.cpp +++ b/audio/XAudio2/XAudio2AudioProvider.cpp @@ -36,6 +36,8 @@ namespace NovelRT::Audio::XAudio2 throw new Exceptions::InitialisationFailureException("Failed to create an output voice!", _hr); } + _masterVoice->SetVolume(1.0f); + XAUDIO2_DEBUG_CONFIGURATION debug{}; debug.TraceMask = XAUDIO2_LOG_ERRORS; _device->SetDebugConfiguration(&debug); @@ -79,6 +81,7 @@ namespace NovelRT::Audio::XAudio2 { _logger->error("Could not create source voice - Code: {hr}", _hr); } + newVoice->SetVolume(1.0f); _sources.emplace(nextSource, newVoice); return nextSource; @@ -102,19 +105,20 @@ namespace NovelRT::Audio::XAudio2 void XAudio2AudioProvider::PauseSource(uint32_t sourceId) { - PlaySource(sourceId); + StopSource(sourceId); } uint32_t XAudio2AudioProvider::SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, AudioSourceContext& context) { - uint32_t nextBuffer = ++_bufferCounter; + //uint32_t nextBuffer = ++_bufferCounter; XAUDIO2_BUFFER xABuffer = { XAUDIO2_END_OF_STREAM, // Flags static_cast(buffer.size()*sizeof(int16_t)), // AudioBytes - static_cast(new byte[buffer.size()*sizeof(int16_t)]) + static_cast(new byte[buffer.size()*sizeof(int16_t)]) //new buffer to copy int16_t* to }; + //Because XAudio2 expects a BYTE*, we'll have to cast it up and copy the data from the provided span :( std::memcpy((void*)(xABuffer.pAudioData), reinterpret_cast(buffer.data()), buffer.size()*sizeof(int16_t)); if(context.Loop) @@ -122,21 +126,20 @@ namespace NovelRT::Audio::XAudio2 xABuffer.LoopCount = XAUDIO2_LOOP_INFINITE; } - _buffers.emplace(nextBuffer, xABuffer); uint32_t sourceId = OpenSource(context); - if(FAILED(_hr = _sources.at(sourceId)->SubmitSourceBuffer(&xABuffer))) { _logger->error("Failed to submit buffer to source {sourceId} - Code: {hr}", sourceId, std::to_string(_hr)); } + _buffers.emplace(sourceId, xABuffer); return sourceId; } void XAudio2AudioProvider::SetSourceProperties(uint32_t sourceId, AudioSourceContext& context) { //volume - if(FAILED(_hr = _sources.at(sourceId)->SetVolume(ConvertToXAudio2VolumeUnits(context.Volume)))) + if(FAILED(_hr = _sources.at(sourceId)->SetVolume(context.Volume))) { _logger->error("Error when setting volume for source {id} - Code: {hr}", sourceId, _hr); } @@ -151,26 +154,42 @@ namespace NovelRT::Audio::XAudio2 { XAUDIO2_VOICE_STATE voiceState; _sources.at(sourceId)->GetState(&voiceState, 0); - return ConvertToAudioSourceState(voiceState); - } - - float XAudio2AudioProvider::ConvertToXAudio2VolumeUnits(float inputVolume) - { - return inputVolume * (XAUDIO2_MAX_VOLUME_LEVEL - (-XAUDIO2_MAX_VOLUME_LEVEL)) + -XAUDIO2_MAX_VOLUME_LEVEL; + AudioSourceState state = ConvertToAudioSourceState(voiceState); + if(state == AudioSourceState::SOURCE_STOPPED) + { + if(voiceState.pCurrentBufferContext == NULL && voiceState.BuffersQueued == 0 && voiceState.SamplesPlayed == 0) + { + auto source = _sources.at(sourceId); + source->Stop(0); + source->SubmitSourceBuffer(&_buffers.at(sourceId)); + } + } + return state; } AudioSourceState XAudio2AudioProvider::ConvertToAudioSourceState(XAUDIO2_VOICE_STATE sourceState) { - switch(sourceState.BuffersQueued) + if(sourceState.BuffersQueued == 0) { - case NULL: + if(sourceState.SamplesPlayed > 0 || (sourceState.pCurrentBufferContext == NULL && sourceState.SamplesPlayed == 0)) { return AudioSourceState::SOURCE_STOPPED; } - default: + else { return AudioSourceState::SOURCE_PLAYING; } } + else + { + if(sourceState.SamplesPlayed > 0) + { + return AudioSourceState::SOURCE_PLAYING; + } + else + { + return AudioSourceState::SOURCE_STOPPED; + } + } } } diff --git a/audio/include/NovelRT/Audio/XAudio2/XAudio2AudioProvider.hpp b/audio/include/NovelRT/Audio/XAudio2/XAudio2AudioProvider.hpp index f17c94441..0cf8c4c46 100644 --- a/audio/include/NovelRT/Audio/XAudio2/XAudio2AudioProvider.hpp +++ b/audio/include/NovelRT/Audio/XAudio2/XAudio2AudioProvider.hpp @@ -22,10 +22,7 @@ namespace NovelRT::Audio::XAudio2 std::map _buffers; HRESULT _hr; - float ConvertToXAudio2VolumeUnits(float inputVolume); AudioSourceState ConvertToAudioSourceState(XAUDIO2_VOICE_STATE sourceState); - // ALenum DetermineChannelFormat(int32_t numberOfChannels); - // void LogOpenALMessages(char level, const char* message); protected: void Dispose() final; diff --git a/src/NovelRT/CMakeLists.txt b/src/NovelRT/CMakeLists.txt index 615f865d2..18053204a 100644 --- a/src/NovelRT/CMakeLists.txt +++ b/src/NovelRT/CMakeLists.txt @@ -68,7 +68,7 @@ target_link_libraries(Engine PUBLIC runtime tbb - OpenAL + $<$:OpenAL> sndfile glfw png diff --git a/thirdparty/OpenAL/CMakeLists.txt b/thirdparty/OpenAL/CMakeLists.txt index a01bbabdf..4e80a47fb 100644 --- a/thirdparty/OpenAL/CMakeLists.txt +++ b/thirdparty/OpenAL/CMakeLists.txt @@ -1,40 +1,43 @@ -include(FetchContent) +if(NOT ${WIN32}) + include(FetchContent) -set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) + set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) -set(ALSOFT_UTILS OFF) -set(ALSOFT_NO_CONFIG_UTIL ON) -set(ALSOFT_EXAMPLES OFF) -set(ALSOFT_INSTALL_EXAMPLES OFF) -set(ALSOFT_INSTALL_UTILS OFF) -set(ALSOFT_TESTS OFF) + set(ALSOFT_UTILS OFF) + set(ALSOFT_NO_CONFIG_UTIL ON) + set(ALSOFT_EXAMPLES OFF) + set(ALSOFT_INSTALL_EXAMPLES OFF) + set(ALSOFT_INSTALL_UTILS OFF) + set(ALSOFT_TESTS OFF) -set(BUILD_SHARED_LIBS ON) -set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS OFF) + set(BUILD_SHARED_LIBS ON) + set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS OFF) -FetchContent_MakeAvailable(OpenAL) -# These warnings are outside our scope as we do not manage the libraries themselves. -# We'll silence the warnings so that output is a little cleaner. -target_compile_options(OpenAL - PRIVATE - $<$:/wd4127> - $<$:/wd4834> + FetchContent_MakeAvailable(OpenAL) - $<$:-Wno-deprecated-copy> + # These warnings are outside our scope as we do not manage the libraries themselves. + # We'll silence the warnings so that output is a little cleaner. + target_compile_options(OpenAL + PRIVATE + $<$:/wd4127> + $<$:/wd4834> - $<$:-Wno-deprecated-copy> + $<$:-Wno-deprecated-copy> - $<$:-Wno-deprecated-copy> -) -target_compile_options(alcommon - PRIVATE - $<$:/wd4127> - $<$:/wd4834> + $<$:-Wno-deprecated-copy> - $<$:-Wno-deprecated-copy> + $<$:-Wno-deprecated-copy> + ) + target_compile_options(alcommon + PRIVATE + $<$:/wd4127> + $<$:/wd4834> - $<$:-Wno-deprecated-copy> + $<$:-Wno-deprecated-copy> - $<$:-Wno-deprecated-copy> -) + $<$:-Wno-deprecated-copy> + + $<$:-Wno-deprecated-copy> + ) +endif() From 2fdc941be075381376b0732c7cdabc8cd4430f04 Mon Sep 17 00:00:00 2001 From: Ken Johnson Date: Wed, 21 Feb 2024 01:47:44 -0500 Subject: [PATCH 20/40] [UNTESTED] Obj-C++ wrapper for AVAudioEngine compiles --- CMakeLists.txt | 4 + .../AVAudioEngineAudioProvider.mm | 168 ++++++++++++++++++ audio/AudioMixer.cpp | 4 + audio/CMakeLists.txt | 30 +++- .../AVAudioEngineAudioProvider.hpp | 50 ++++++ samples/AudioEcsSample/CMakeLists.txt | 4 +- src/NovelRT/CMakeLists.txt | 9 +- thirdparty/OpenAL/CMakeLists.txt | 2 +- 8 files changed, 259 insertions(+), 12 deletions(-) create mode 100644 audio/AVAudioEngine/AVAudioEngineAudioProvider.mm create mode 100644 audio/include/NovelRT/Audio/AVAudioEngine/AVAudioEngineAudioProvider.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index ef7238c85..d19efd5d1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,12 +34,16 @@ if(NOT DEFINED NOVELRT_TARGET) set(NOVELRT_TARGET "Win32" CACHE STRING "") elseif(APPLE) set(NOVELRT_TARGET "macOS" CACHE STRING "") + find_library(AVFOUNDATION_LIB AVFoundation) + find_library(FOUNDATION_LIB Foundation) + find_library(OBJC_LIB ObjC) elseif(UNIX) set(NOVELRT_TARGET "Linux" CACHE STRING "") else() set(NOVELRT_TARGET "Unknown" CACHE STRING "") endif() endif() +message("Using OS: ${NOVELRT_TARGET}") # # Prepend so that our FindVulkan gets picked up first when needed diff --git a/audio/AVAudioEngine/AVAudioEngineAudioProvider.mm b/audio/AVAudioEngine/AVAudioEngineAudioProvider.mm new file mode 100644 index 000000000..fae3149ea --- /dev/null +++ b/audio/AVAudioEngine/AVAudioEngineAudioProvider.mm @@ -0,0 +1,168 @@ +// Copyright © Matt Jones and Contributors. Licensed under the MIT Licence (MIT). See LICENCE.md in the repository root +// for more information. +#include +#include +#import +#import +#import +#import + + +namespace NovelRT::Audio::AVAudioEngine +{ + AVAudioEngineAudioProvider::AVAudioEngineAudioProvider(): + _impl(nullptr), + _buffers(std::map()), + _sources(std::map()), + _sourceEQUnits(std::map()), + _sourceStates(std::map()), + _logger(spdlog::stdout_color_mt("AVAudioEngine")) + { + //Logger init + _logger->set_level(spdlog::level::debug); + + //Device and Context Init + @try + { + ::NSError* err; + _impl = [[::AVAudioEngine alloc] init]; + [(::AVAudioEngine*)_impl prepare]; + if([(::AVAudioEngine*)_impl startAndReturnError: &err]) + { + _logger->error([err.localizedDescription UTF8String]); + return; + } + } + @catch(::NSException* ex) + { + std::string err = std::string([ex.reason UTF8String]); + _logger->error(err); + } + } + + AVAudioEngineAudioProvider::~AVAudioEngineAudioProvider() + { + Dispose(); + [(::AVAudioEngine*)_impl stop]; + [(::AVAudioEngine*)_impl release]; + } + + void AVAudioEngineAudioProvider::Dispose() + { + for(auto [id, source] : _sources) + { + [(::AVAudioPlayerNode*)source stop]; + [(::AVAudioPlayerNode*)source release]; + } + _sources.clear(); + + for(auto [id, source] : _sourceEQUnits) + { + [(::AVAudioUnitEQ*)source release]; + } + _sourceEQUnits.clear(); + + for(auto [id, buffer] : _buffers) + { + [(::AVAudioPCMBuffer*)buffer release]; + } + _buffers.clear(); + } + + uint32_t AVAudioEngineAudioProvider::OpenSource(AudioSourceContext& context) + { + uint32_t nextSource = ++_sourceCounter; + ::AVAudioPlayerNode* node = [[::AVAudioPlayerNode alloc] init]; + [(::AVAudioEngine*)_impl attachNode:node]; + _sources.emplace(nextSource, node); + _sourceStates.emplace(nextSource, AudioSourceState::SOURCE_STOPPED); + + ::AVAudioUnitEQ* eq = [[::AVAudioUnitEQ alloc] initWithNumberOfBands: 1]; + [(::AVAudioEngine*)_impl attachNode:eq]; + _sourceEQUnits.emplace(nextSource, eq); + + ::AVAudioFormat* format = [[::AVAudioFormat alloc] initWithCommonFormat:AVAudioCommonFormat::AVAudioPCMFormatInt16 sampleRate:context.SampleRate channels:context.Channels interleaved:false]; + ::AVAudioMixerNode* mixerNode = ((::AVAudioEngine*)_impl).mainMixerNode; + [(::AVAudioEngine*)_impl connect:node to:eq format:format]; + [(::AVAudioEngine*)_impl connect:eq to:mixerNode format:format]; + + return nextSource; + } + + void AVAudioEngineAudioProvider::PlaySource(uint32_t sourceId) + { + ::AVAudioPlayerNode* node = _sources.at(sourceId); + + if(!node.playing) + { + [(::AVAudioPlayerNode*)node play]; + } + + + _sourceStates[sourceId] = AudioSourceState::SOURCE_PLAYING; + + } + + void AVAudioEngineAudioProvider::StopSource(uint32_t sourceId) + { + ::AVAudioPlayerNode* node = _sources.at(sourceId); + if(node.playing) + { + [(::AVAudioPlayerNode*)node stop]; + } + _sourceStates[sourceId] = AudioSourceState::SOURCE_STOPPED; + } + + void AVAudioEngineAudioProvider::PauseSource(uint32_t sourceId) + { + ::AVAudioPlayerNode* node = _sources.at(sourceId); + if(node.playing) + { + [(::AVAudioPlayerNode*)node pause]; + } + _sourceStates[sourceId] = AudioSourceState::SOURCE_PAUSED; + } + + uint32_t AVAudioEngineAudioProvider::SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, AudioSourceContext& context) + { + ::AVAudioFormat* format = [[::AVAudioFormat alloc] initWithCommonFormat:AVAudioCommonFormat::AVAudioPCMFormatInt16 sampleRate:context.SampleRate channels:context.Channels interleaved:false]; + AudioBufferList abl; + abl.mNumberBuffers = 1; + abl.mBuffers[0].mData = (void *)buffer.data(); + abl.mBuffers[0].mNumberChannels = context.Channels; + abl.mBuffers[0].mDataByteSize = buffer.size() * sizeof(int16_t); + ::AVAudioPCMBuffer* pcmBuffer = [[::AVAudioPCMBuffer alloc] initWithPCMFormat:format bufferListNoCopy:&abl deallocator:NULL]; + + uint32_t sourceId = OpenSource(context); + + ::AVAudioPlayerNode* node = _sources.at(sourceId); + // AVAudioPlayerNodeBufferOptions options = AVAudioPlayerNodeBufferLoops; + + if(context.Loop) + { + [(::AVAudioPlayerNode*)node scheduleBuffer: pcmBuffer atTime: nil options: AVAudioPlayerNodeBufferLoops completionCallbackType: AVAudioPlayerNodeCompletionDataPlayedBack completionHandler: nil]; + } + else + { + [(::AVAudioPlayerNode*)node scheduleBuffer: pcmBuffer completionHandler: nil]; + } + _buffers.emplace(sourceId, pcmBuffer); + + return sourceId; + } + + void AVAudioEngineAudioProvider::SetSourceProperties(uint32_t sourceId, AudioSourceContext& context) + { + ::AVAudioUnitEQ* eq = _sourceEQUnits.at(sourceId); + eq.globalGain = context.Volume; + } + + AudioSourceState AVAudioEngineAudioProvider::GetSourceState(uint32_t sourceId) + { + return _sourceStates.at(sourceId); + } + + + + +} diff --git a/audio/AudioMixer.cpp b/audio/AudioMixer.cpp index 762bcc0f9..f99119bd2 100644 --- a/audio/AudioMixer.cpp +++ b/audio/AudioMixer.cpp @@ -5,6 +5,8 @@ //Conditional #if defined(_WIN32) #include +#elif __APPLE__ +#include #else #include #endif @@ -15,6 +17,8 @@ namespace NovelRT::Audio _sourceContextCache = std::map(); #if defined(_WIN32) _audioProvider = std::make_unique(); +#elif defined(__APPLE__) + _audioProvider = std::make_unique(); #else _audioProvider = std::make_unique(); #endif diff --git a/audio/CMakeLists.txt b/audio/CMakeLists.txt index ced6b14da..7710271bc 100644 --- a/audio/CMakeLists.txt +++ b/audio/CMakeLists.txt @@ -1,8 +1,8 @@ add_library(NovelRT-Audio STATIC AudioMixer.cpp - $<$:XAudio2/XAudio2AudioProvider.cpp> - $<$:OpenAL/OpenALAudioProvider.cpp> - $<$:OpenAL/OpenALAudioProvider.cpp> + $<$>:XAudio2/XAudio2AudioProvider.cpp> + $<$>:AVAudioEngine/AVAudioEngineAudioProvider.mm> + $<$>:OpenAL/OpenALAudioProvider.cpp> ) target_sources(NovelRT-Audio @@ -13,9 +13,17 @@ target_sources(NovelRT-Audio FILES include/NovelRT/Audio/AudioMixer.hpp include/NovelRT/Audio/IAudioProvider.hpp - $<$:include/NovelRT/Audio/XAudio2/XAudio2AudioProvider.hpp> - $<$:include/NovelRT/Audio/OpenAL/OpenALAudioProvider.hpp> - $<$:include/NovelRT/Audio/OpenAL/OpenALAudioProvider.hpp> +) + +target_sources(NovelRT-Audio + PRIVATE + FILE_SET private_headers + TYPE HEADERS + BASE_DIRS include + FILES + $<$>:include/NovelRT/Audio/XAudio2/XAudio2AudioProvider.hpp> + $<$>:include/NovelRT/Audio/OpenAL/OpenALAudioProvider.hpp> + $<$>:include/NovelRT/Audio/AVAudioEngine/AVAudioEngineAudioProvider.hpp> ) set_target_properties(NovelRT-Audio @@ -53,10 +61,16 @@ target_include_directories(NovelRT-Audio PRIVATE "$:OpenAL> - $<$:OpenAL> + $<$>:OpenAL> ) +if(${NOVELRT_TARGET} STREQUAL "macOS") + target_link_libraries(NovelRT-Audio + PRIVATE + "-framework AVFoundation" + ) +endif() + install( TARGETS NovelRT-Audio EXPORT NovelRTConfig diff --git a/audio/include/NovelRT/Audio/AVAudioEngine/AVAudioEngineAudioProvider.hpp b/audio/include/NovelRT/Audio/AVAudioEngine/AVAudioEngineAudioProvider.hpp new file mode 100644 index 000000000..13d5ec9b1 --- /dev/null +++ b/audio/include/NovelRT/Audio/AVAudioEngine/AVAudioEngineAudioProvider.hpp @@ -0,0 +1,50 @@ +// Copyright © Matt Jones and Contributors. Licensed under the MIT Licence (MIT). See LICENCE.md in the repository root +// for more information. +#pragma once + +#include +#include +#include +#include + +#ifdef __OBJC__ + @class AVAudioEngine; + @class AVAudioPlayerNode; + @class AVAudioPCMBuffer; + @class AVAudioUnitEQ; +#else + typedef struct objc_object AVAudioEngine; + typedef struct objc_object AVAudioPlayerNode; + typedef struct objc_object AVAudioPCMBuffer; + typedef struct objc_object AVAudioUnitEQ; +#endif + +namespace NovelRT::Audio::AVAudioEngine +{ + class AVAudioEngineAudioProvider : public IAudioProvider + { + private: + ::AVAudioEngine* _impl; // represents Obj-C object + std::map _sources; + std::map _sourceEQUnits; + std::map _buffers; + std::map _sourceStates; + std::shared_ptr _logger; + uint32_t _sourceCounter = 0; + + protected: + void Dispose() final; + uint32_t OpenSource(AudioSourceContext& context) final; + + public: + AVAudioEngineAudioProvider(); + void PlaySource(uint32_t sourceId) final; + void StopSource(uint32_t sourceId) final; + void PauseSource(uint32_t sourceId) final; + void SetSourceProperties(uint32_t sourceId, AudioSourceContext& context) final; + uint32_t SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, AudioSourceContext& context) final; + AudioSourceState GetSourceState(uint32_t id) final; + + ~AVAudioEngineAudioProvider() final; + }; +} diff --git a/samples/AudioEcsSample/CMakeLists.txt b/samples/AudioEcsSample/CMakeLists.txt index f6c1a9d52..3c3b78193 100644 --- a/samples/AudioEcsSample/CMakeLists.txt +++ b/samples/AudioEcsSample/CMakeLists.txt @@ -24,10 +24,10 @@ target_link_libraries(AudioEcsSample copy_build_products(AudioEcsSample DEPENDENCY Resources - TARGET_LOCATION $,$/../Resources,$/Resources> + TARGET_LOCATION $>,$/../Resources,$/Resources> ) -if(APPLE) +if(${NOVELRT_TARGET} STREQUAL "macOS") copy_runtime_dependencies(AudioEcsSample DEPENDENCY Engine LIBRARY Vulkan::MoltenVK diff --git a/src/NovelRT/CMakeLists.txt b/src/NovelRT/CMakeLists.txt index 18053204a..766d91baa 100644 --- a/src/NovelRT/CMakeLists.txt +++ b/src/NovelRT/CMakeLists.txt @@ -68,7 +68,6 @@ target_link_libraries(Engine PUBLIC runtime tbb - $<$:OpenAL> sndfile glfw png @@ -80,11 +79,19 @@ target_link_libraries(Engine FLAC opus ogg + $<$>:OpenAL> NovelRT-Graphics NovelRT-Audio NovelRT-Input ) +if(${NOVELRT_TARGET} STREQUAL "macOS") +target_link_libraries(Engine + PRIVATE + "-framework AVFoundation" +) +endif() + if(NOVELRT_INSTALL) install( TARGETS Engine diff --git a/thirdparty/OpenAL/CMakeLists.txt b/thirdparty/OpenAL/CMakeLists.txt index 4e80a47fb..fbf96edf2 100644 --- a/thirdparty/OpenAL/CMakeLists.txt +++ b/thirdparty/OpenAL/CMakeLists.txt @@ -1,4 +1,4 @@ -if(NOT ${WIN32}) +if(${NOVELRT_TARGET} STREQUAL Linux) include(FetchContent) set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) From 06d98fb7a531b51f2f1edf26f662f21b0696be50 Mon Sep 17 00:00:00 2001 From: Ken Johnson Date: Wed, 21 Feb 2024 13:24:08 -0500 Subject: [PATCH 21/40] 50% success - usually runs with AVAudioEngine, sometimes crashes due to interruptions unhandled --- .../AVAudioEngineAudioProvider.mm | 132 ++++++++++++------ .../AVAudioEngineAudioProvider.hpp | 11 +- samples/AudioEcsSample/CMakeLists.txt | 4 + 3 files changed, 107 insertions(+), 40 deletions(-) diff --git a/audio/AVAudioEngine/AVAudioEngineAudioProvider.mm b/audio/AVAudioEngine/AVAudioEngineAudioProvider.mm index fae3149ea..f3cc59278 100644 --- a/audio/AVAudioEngine/AVAudioEngineAudioProvider.mm +++ b/audio/AVAudioEngine/AVAudioEngineAudioProvider.mm @@ -7,11 +7,9 @@ #import #import - namespace NovelRT::Audio::AVAudioEngine { AVAudioEngineAudioProvider::AVAudioEngineAudioProvider(): - _impl(nullptr), _buffers(std::map()), _sources(std::map()), _sourceEQUnits(std::map()), @@ -20,23 +18,40 @@ { //Logger init _logger->set_level(spdlog::level::debug); - + //Device and Context Init - @try + @try { ::NSError* err; - _impl = [[::AVAudioEngine alloc] init]; - [(::AVAudioEngine*)_impl prepare]; - if([(::AVAudioEngine*)_impl startAndReturnError: &err]) - { - _logger->error([err.localizedDescription UTF8String]); - return; - } + _logger->debug("calling alloc and init"); + _impl = [::AVAudioEngine new]; + + + _logger->debug("getting mainMixerNode and format"); + + _mixerFormat = [_impl.mainMixerNode outputFormatForBus:0]; + _logger->debug("Retrieved format - Channels {0}, SampleRate {1}", _mixerFormat.channelCount, _mixerFormat.sampleRate); + // ::AVAudioMixerNode* mixNode = [[::AVAudioMixerNode alloc] init]; + // _logger->debug("attaching mixer"); + // [(::AVAudioEngine*)_impl attachNode:mixNode]; + // _logger->debug("connecting nodes"); + // [(::AVAudioEngine*)_impl connect:mixNode to:_impl.outputNode format:_mixerFormat]; + //::AVAudioOutputNode* outputNode = ((::AVAudioEngine*)_impl).outputNode; + + + + // if([(::AVAudioEngine*)_impl startAndReturnError: &err]) + // { + // std::string error = std::string([err.localizedDescription UTF8String]); + // _logger->error(error); + // throw new Exceptions::InitialisationFailureException("Failed to initialise AVAudioEngine!", error); + // } } @catch(::NSException* ex) { std::string err = std::string([ex.reason UTF8String]); _logger->error(err); + throw new Exceptions::InitialisationFailureException("Failed to initialise AVAudioEngine!", err); } } @@ -71,34 +86,72 @@ uint32_t AVAudioEngineAudioProvider::OpenSource(AudioSourceContext& context) { + unused(context); + return _sourceCounter; + } + + uint32_t AVAudioEngineAudioProvider::OpenSourceInternal(AudioSourceContext& context, ::AVAudioPCMBuffer* buffer, ::AVAudioFormat* format) + { + if(format == nullptr) + { + _logger->error("SCREEEEEEEEEEEEEEEEEEEEEEEECH"); + } + uint32_t nextSource = ++_sourceCounter; - ::AVAudioPlayerNode* node = [[::AVAudioPlayerNode alloc] init]; - [(::AVAudioEngine*)_impl attachNode:node]; + ::AVAudioPlayerNode* node = [::AVAudioPlayerNode new]; + [_impl attachNode:node]; _sources.emplace(nextSource, node); _sourceStates.emplace(nextSource, AudioSourceState::SOURCE_STOPPED); ::AVAudioUnitEQ* eq = [[::AVAudioUnitEQ alloc] initWithNumberOfBands: 1]; - [(::AVAudioEngine*)_impl attachNode:eq]; + [_impl attachNode:eq]; _sourceEQUnits.emplace(nextSource, eq); - ::AVAudioFormat* format = [[::AVAudioFormat alloc] initWithCommonFormat:AVAudioCommonFormat::AVAudioPCMFormatInt16 sampleRate:context.SampleRate channels:context.Channels interleaved:false]; - ::AVAudioMixerNode* mixerNode = ((::AVAudioEngine*)_impl).mainMixerNode; - [(::AVAudioEngine*)_impl connect:node to:eq format:format]; - [(::AVAudioEngine*)_impl connect:eq to:mixerNode format:format]; - + _logger->debug("Connecting source {0} to EQ", nextSource); + [(::AVAudioEngine*)_impl connect:node to:eq format:nil]; + _logger->debug("Connecting source {0} EQ to mixer", nextSource); + [(::AVAudioEngine*)_impl connect:eq to:_impl.mainMixerNode format:nil]; + + //[eq play] + // if(context.Loop) + // { + // _logger->debug("Scheduling looping pcm buffer for source {0}", nextSource); + // [node scheduleBuffer: buffer atTime: nil options: AVAudioPlayerNodeBufferLoops completionCallbackType: AVAudioPlayerNodeCompletionDataPlayedBack completionHandler: nil]; + // } + // else + // { + // _logger->debug("Scheduling pcm buffer for source {0}", nextSource); + // [node scheduleBuffer: buffer completionHandler: nil]; + // } + _buffers.emplace(nextSource, buffer); + return nextSource; } void AVAudioEngineAudioProvider::PlaySource(uint32_t sourceId) { + if(!_impl.running) + { + _logger->debug("filling up gas tank..."); + [(::AVAudioEngine*)_impl prepare]; + ::NSError* err; + _logger->debug("starting engine.... VRROOOOOOOMMMMM...."); + [_impl startAndReturnError: &err]; + if(!_impl.running) + { + _logger->error("Could not start engine: {0}", std::string([err.localizedDescription UTF8String])); + } + } + ::AVAudioPlayerNode* node = _sources.at(sourceId); - + if(!node.playing) { - [(::AVAudioPlayerNode*)node play]; + [node scheduleBuffer: _buffers.at(sourceId) completionHandler: nil]; + [node play]; } - - + + _sourceStates[sourceId] = AudioSourceState::SOURCE_PLAYING; } @@ -125,30 +178,35 @@ uint32_t AVAudioEngineAudioProvider::SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, AudioSourceContext& context) { - ::AVAudioFormat* format = [[::AVAudioFormat alloc] initWithCommonFormat:AVAudioCommonFormat::AVAudioPCMFormatInt16 sampleRate:context.SampleRate channels:context.Channels interleaved:false]; + _logger->debug("Loading audio buffer - SampleRate: {0}, Channels: {1}", context.SampleRate, context.Channels); + ::AVAudioFormat* format = [[::AVAudioFormat alloc] initWithCommonFormat:AVAudioCommonFormat::AVAudioPCMFormatInt16 sampleRate:context.SampleRate channels:context.Channels interleaved:true]; AudioBufferList abl; abl.mNumberBuffers = 1; abl.mBuffers[0].mData = (void *)buffer.data(); abl.mBuffers[0].mNumberChannels = context.Channels; abl.mBuffers[0].mDataByteSize = buffer.size() * sizeof(int16_t); + ::AVAudioPCMBuffer* pcmBuffer = [[::AVAudioPCMBuffer alloc] initWithPCMFormat:format bufferListNoCopy:&abl deallocator:NULL]; - - uint32_t sourceId = OpenSource(context); - - ::AVAudioPlayerNode* node = _sources.at(sourceId); - // AVAudioPlayerNodeBufferOptions options = AVAudioPlayerNodeBufferLoops; + if(context.SampleRate != _mixerFormat.sampleRate) + { + ::AVAudioConverter* convert = [[::AVAudioConverter alloc] initFromFormat:format toFormat:_mixerFormat]; + + ::AVAudioPCMBuffer* newBuffer = [[::AVAudioPCMBuffer alloc] initWithPCMFormat:format frameCapacity:pcmBuffer.frameCapacity]; - if(context.Loop) + _logger->debug("converting..."); + [convert convertToBuffer:newBuffer error:nil withInputFromBlock: ^(::AVAudioPacketCount inNumberOfPackets, ::AVAudioConverterInputStatus *outStatus) { - [(::AVAudioPlayerNode*)node scheduleBuffer: pcmBuffer atTime: nil options: AVAudioPlayerNodeBufferLoops completionCallbackType: AVAudioPlayerNodeCompletionDataPlayedBack completionHandler: nil]; + *outStatus = AVAudioConverterInputStatus::AVAudioConverterInputStatus_HaveData; + return pcmBuffer; + }]; + // [convert convertToBuffer:newBuffer fromBuffer:pcmBuffer error:nil]; + + return OpenSourceInternal(context, newBuffer, format); } else { - [(::AVAudioPlayerNode*)node scheduleBuffer: pcmBuffer completionHandler: nil]; + return OpenSourceInternal(context, pcmBuffer, format); } - _buffers.emplace(sourceId, pcmBuffer); - - return sourceId; } void AVAudioEngineAudioProvider::SetSourceProperties(uint32_t sourceId, AudioSourceContext& context) @@ -161,8 +219,4 @@ { return _sourceStates.at(sourceId); } - - - - } diff --git a/audio/include/NovelRT/Audio/AVAudioEngine/AVAudioEngineAudioProvider.hpp b/audio/include/NovelRT/Audio/AVAudioEngine/AVAudioEngineAudioProvider.hpp index 13d5ec9b1..872700a05 100644 --- a/audio/include/NovelRT/Audio/AVAudioEngine/AVAudioEngineAudioProvider.hpp +++ b/audio/include/NovelRT/Audio/AVAudioEngine/AVAudioEngineAudioProvider.hpp @@ -7,24 +7,31 @@ #include #include + #ifdef __OBJC__ @class AVAudioEngine; @class AVAudioPlayerNode; @class AVAudioPCMBuffer; @class AVAudioUnitEQ; + @class AVAudioFormat; + ::AVAudioEngine* _impl; // represents Obj-C object + ::AVAudioFormat* _mixerFormat; #else typedef struct objc_object AVAudioEngine; typedef struct objc_object AVAudioPlayerNode; typedef struct objc_object AVAudioPCMBuffer; typedef struct objc_object AVAudioUnitEQ; + typedef struct objc_object AVAudioFormat; #endif + + namespace NovelRT::Audio::AVAudioEngine { class AVAudioEngineAudioProvider : public IAudioProvider { private: - ::AVAudioEngine* _impl; // represents Obj-C object + std::map _sources; std::map _sourceEQUnits; std::map _buffers; @@ -32,6 +39,8 @@ namespace NovelRT::Audio::AVAudioEngine std::shared_ptr _logger; uint32_t _sourceCounter = 0; + uint32_t OpenSourceInternal(AudioSourceContext& context, ::AVAudioPCMBuffer* buffer, ::AVAudioFormat* format); + protected: void Dispose() final; uint32_t OpenSource(AudioSourceContext& context) final; diff --git a/samples/AudioEcsSample/CMakeLists.txt b/samples/AudioEcsSample/CMakeLists.txt index 3c3b78193..c5123e152 100644 --- a/samples/AudioEcsSample/CMakeLists.txt +++ b/samples/AudioEcsSample/CMakeLists.txt @@ -28,6 +28,10 @@ copy_build_products(AudioEcsSample ) if(${NOVELRT_TARGET} STREQUAL "macOS") + target_link_libraries(AudioEcsSample + PRIVATE + "-framework AVFoundation" + ) copy_runtime_dependencies(AudioEcsSample DEPENDENCY Engine LIBRARY Vulkan::MoltenVK From d59e67a57a49039e60b43a379ef5948e16311d6a Mon Sep 17 00:00:00 2001 From: Ken Johnson Date: Sun, 10 Mar 2024 19:13:09 -0400 Subject: [PATCH 22/40] [WIP, WORKING] Switched to 32-bit float audio and static samplerate. Need to finish standardising samplerate conversion support and formats for XAudio and OpenAL --- .../AVAudioEngineAudioProvider.mm | 104 +++++++----------- audio/AudioMixer.cpp | 2 +- .../AVAudioEngineAudioProvider.hpp | 4 +- audio/include/NovelRT/Audio/AudioMixer.hpp | 2 +- .../include/NovelRT/Audio/IAudioProvider.hpp | 2 +- .../ResourceManagement/AudioMetadata.h | 2 +- src/NovelRT/CMakeLists.txt | 1 + src/NovelRT/Ecs/Audio/AudioSystem.cpp | 3 +- .../Desktop/DesktopResourceLoader.cpp | 36 +++++- thirdparty/CMakeLists.txt | 9 +- thirdparty/samplerate/CMakeLists.txt | 8 ++ 11 files changed, 98 insertions(+), 75 deletions(-) create mode 100644 thirdparty/samplerate/CMakeLists.txt diff --git a/audio/AVAudioEngine/AVAudioEngineAudioProvider.mm b/audio/AVAudioEngine/AVAudioEngineAudioProvider.mm index f3cc59278..b865810fc 100644 --- a/audio/AVAudioEngine/AVAudioEngineAudioProvider.mm +++ b/audio/AVAudioEngine/AVAudioEngineAudioProvider.mm @@ -14,6 +14,7 @@ _sources(std::map()), _sourceEQUnits(std::map()), _sourceStates(std::map()), + _sourceContexts(std::map()), _logger(spdlog::stdout_color_mt("AVAudioEngine")) { //Logger init @@ -25,27 +26,6 @@ ::NSError* err; _logger->debug("calling alloc and init"); _impl = [::AVAudioEngine new]; - - - _logger->debug("getting mainMixerNode and format"); - - _mixerFormat = [_impl.mainMixerNode outputFormatForBus:0]; - _logger->debug("Retrieved format - Channels {0}, SampleRate {1}", _mixerFormat.channelCount, _mixerFormat.sampleRate); - // ::AVAudioMixerNode* mixNode = [[::AVAudioMixerNode alloc] init]; - // _logger->debug("attaching mixer"); - // [(::AVAudioEngine*)_impl attachNode:mixNode]; - // _logger->debug("connecting nodes"); - // [(::AVAudioEngine*)_impl connect:mixNode to:_impl.outputNode format:_mixerFormat]; - //::AVAudioOutputNode* outputNode = ((::AVAudioEngine*)_impl).outputNode; - - - - // if([(::AVAudioEngine*)_impl startAndReturnError: &err]) - // { - // std::string error = std::string([err.localizedDescription UTF8String]); - // _logger->error(error); - // throw new Exceptions::InitialisationFailureException("Failed to initialise AVAudioEngine!", error); - // } } @catch(::NSException* ex) { @@ -82,6 +62,8 @@ [(::AVAudioPCMBuffer*)buffer release]; } _buffers.clear(); + + _sourceContexts.clear(); } uint32_t AVAudioEngineAudioProvider::OpenSource(AudioSourceContext& context) @@ -92,16 +74,12 @@ uint32_t AVAudioEngineAudioProvider::OpenSourceInternal(AudioSourceContext& context, ::AVAudioPCMBuffer* buffer, ::AVAudioFormat* format) { - if(format == nullptr) - { - _logger->error("SCREEEEEEEEEEEEEEEEEEEEEEEECH"); - } - uint32_t nextSource = ++_sourceCounter; ::AVAudioPlayerNode* node = [::AVAudioPlayerNode new]; [_impl attachNode:node]; _sources.emplace(nextSource, node); _sourceStates.emplace(nextSource, AudioSourceState::SOURCE_STOPPED); + _sourceContexts.emplace(nextSource, context); ::AVAudioUnitEQ* eq = [[::AVAudioUnitEQ alloc] initWithNumberOfBands: 1]; [_impl attachNode:eq]; @@ -111,18 +89,6 @@ [(::AVAudioEngine*)_impl connect:node to:eq format:nil]; _logger->debug("Connecting source {0} EQ to mixer", nextSource); [(::AVAudioEngine*)_impl connect:eq to:_impl.mainMixerNode format:nil]; - - //[eq play] - // if(context.Loop) - // { - // _logger->debug("Scheduling looping pcm buffer for source {0}", nextSource); - // [node scheduleBuffer: buffer atTime: nil options: AVAudioPlayerNodeBufferLoops completionCallbackType: AVAudioPlayerNodeCompletionDataPlayedBack completionHandler: nil]; - // } - // else - // { - // _logger->debug("Scheduling pcm buffer for source {0}", nextSource); - // [node scheduleBuffer: buffer completionHandler: nil]; - // } _buffers.emplace(nextSource, buffer); return nextSource; @@ -147,13 +113,32 @@ if(!node.playing) { - [node scheduleBuffer: _buffers.at(sourceId) completionHandler: nil]; + auto lambda = [this, sourceId = sourceId]() + { + this->UpdateSourceState(sourceId, AudioSourceState::SOURCE_STOPPED); + }; + if(_sourceContexts.at(sourceId).Loop) + { + _logger->debug("Looping source ID {0}", sourceId); + [node scheduleBuffer: _buffers.at(sourceId) atTime: nil options: AVAudioPlayerNodeBufferLoops completionHandler: nil]; + } + else + { + [node scheduleBuffer: _buffers.at(sourceId) completionHandler: lambda]; + } + + + + [node play]; } - _sourceStates[sourceId] = AudioSourceState::SOURCE_PLAYING; + } + void AVAudioEngineAudioProvider::UpdateSourceState(uint32_t sourceId, AudioSourceState state) + { + _sourceStates[sourceId] = state; } void AVAudioEngineAudioProvider::StopSource(uint32_t sourceId) @@ -176,41 +161,34 @@ _sourceStates[sourceId] = AudioSourceState::SOURCE_PAUSED; } - uint32_t AVAudioEngineAudioProvider::SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, AudioSourceContext& context) + uint32_t AVAudioEngineAudioProvider::SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, AudioSourceContext& context) { _logger->debug("Loading audio buffer - SampleRate: {0}, Channels: {1}", context.SampleRate, context.Channels); - ::AVAudioFormat* format = [[::AVAudioFormat alloc] initWithCommonFormat:AVAudioCommonFormat::AVAudioPCMFormatInt16 sampleRate:context.SampleRate channels:context.Channels interleaved:true]; + + uint32_t frameCap = buffer.size() * sizeof(float); + ::AVAudioFormat* format = [[::AVAudioFormat alloc] initWithCommonFormat:AVAudioCommonFormat::AVAudioPCMFormatFloat32 sampleRate:44100 channels:context.Channels interleaved:true]; + ::AVAudioFormat* deformat = [[::AVAudioFormat alloc] initWithCommonFormat:AVAudioCommonFormat::AVAudioPCMFormatFloat32 sampleRate:44100 channels:context.Channels interleaved:false]; AudioBufferList abl; abl.mNumberBuffers = 1; - abl.mBuffers[0].mData = (void *)buffer.data(); + abl.mBuffers[0].mData = ((void *)new Byte[frameCap]); abl.mBuffers[0].mNumberChannels = context.Channels; - abl.mBuffers[0].mDataByteSize = buffer.size() * sizeof(int16_t); - - ::AVAudioPCMBuffer* pcmBuffer = [[::AVAudioPCMBuffer alloc] initWithPCMFormat:format bufferListNoCopy:&abl deallocator:NULL]; - if(context.SampleRate != _mixerFormat.sampleRate) - { - ::AVAudioConverter* convert = [[::AVAudioConverter alloc] initFromFormat:format toFormat:_mixerFormat]; + abl.mBuffers[0].mDataByteSize = frameCap; - ::AVAudioPCMBuffer* newBuffer = [[::AVAudioPCMBuffer alloc] initWithPCMFormat:format frameCapacity:pcmBuffer.frameCapacity]; + std::memcpy((void*)abl.mBuffers[0].mData, reinterpret_cast(buffer.data()), frameCap); - _logger->debug("converting..."); - [convert convertToBuffer:newBuffer error:nil withInputFromBlock: ^(::AVAudioPacketCount inNumberOfPackets, ::AVAudioConverterInputStatus *outStatus) - { - *outStatus = AVAudioConverterInputStatus::AVAudioConverterInputStatus_HaveData; - return pcmBuffer; - }]; - // [convert convertToBuffer:newBuffer fromBuffer:pcmBuffer error:nil]; + ::AVAudioPCMBuffer* pcmBuffer = [[::AVAudioPCMBuffer alloc] initWithPCMFormat:format bufferListNoCopy:&abl deallocator:NULL]; - return OpenSourceInternal(context, newBuffer, format); - } - else - { - return OpenSourceInternal(context, pcmBuffer, format); - } + ::AVAudioConverter* convert = [[::AVAudioConverter alloc] initFromFormat:format toFormat:deformat]; + ::AVAudioPCMBuffer* deinterleavedBuffer = [[::AVAudioPCMBuffer alloc] initWithPCMFormat:deformat frameCapacity:pcmBuffer.frameCapacity]; + [convert convertToBuffer:deinterleavedBuffer fromBuffer:pcmBuffer error:nil]; + + return OpenSourceInternal(context, deinterleavedBuffer, format); } void AVAudioEngineAudioProvider::SetSourceProperties(uint32_t sourceId, AudioSourceContext& context) { + _sourceContexts[sourceId] = context; + ::AVAudioUnitEQ* eq = _sourceEQUnits.at(sourceId); eq.globalGain = context.Volume; } diff --git a/audio/AudioMixer.cpp b/audio/AudioMixer.cpp index f99119bd2..4c3a6d21c 100644 --- a/audio/AudioMixer.cpp +++ b/audio/AudioMixer.cpp @@ -24,7 +24,7 @@ namespace NovelRT::Audio #endif } - uint32_t AudioMixer::SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, int32_t channelCount, int32_t originalSampleRate) + uint32_t AudioMixer::SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, int32_t channelCount, int32_t originalSampleRate) { auto newContext = AudioSourceContext{}; newContext.Channels = channelCount; diff --git a/audio/include/NovelRT/Audio/AVAudioEngine/AVAudioEngineAudioProvider.hpp b/audio/include/NovelRT/Audio/AVAudioEngine/AVAudioEngineAudioProvider.hpp index 872700a05..034ff85eb 100644 --- a/audio/include/NovelRT/Audio/AVAudioEngine/AVAudioEngineAudioProvider.hpp +++ b/audio/include/NovelRT/Audio/AVAudioEngine/AVAudioEngineAudioProvider.hpp @@ -36,6 +36,7 @@ namespace NovelRT::Audio::AVAudioEngine std::map _sourceEQUnits; std::map _buffers; std::map _sourceStates; + std::map _sourceContexts; std::shared_ptr _logger; uint32_t _sourceCounter = 0; @@ -44,6 +45,7 @@ namespace NovelRT::Audio::AVAudioEngine protected: void Dispose() final; uint32_t OpenSource(AudioSourceContext& context) final; + void UpdateSourceState(uint32_t sourceId, AudioSourceState state); public: AVAudioEngineAudioProvider(); @@ -51,7 +53,7 @@ namespace NovelRT::Audio::AVAudioEngine void StopSource(uint32_t sourceId) final; void PauseSource(uint32_t sourceId) final; void SetSourceProperties(uint32_t sourceId, AudioSourceContext& context) final; - uint32_t SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, AudioSourceContext& context) final; + uint32_t SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, AudioSourceContext& context) final; AudioSourceState GetSourceState(uint32_t id) final; ~AVAudioEngineAudioProvider() final; diff --git a/audio/include/NovelRT/Audio/AudioMixer.hpp b/audio/include/NovelRT/Audio/AudioMixer.hpp index fcc3b298d..ed569a948 100644 --- a/audio/include/NovelRT/Audio/AudioMixer.hpp +++ b/audio/include/NovelRT/Audio/AudioMixer.hpp @@ -21,7 +21,7 @@ namespace NovelRT::Audio public: void Initialise(); - uint32_t SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, int32_t channelCount, int32_t originalSampleRate); + uint32_t SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, int32_t channelCount, int32_t originalSampleRate); void PlaySource(uint32_t id); void StopSource(uint32_t id); void PauseSource(uint32_t id); diff --git a/audio/include/NovelRT/Audio/IAudioProvider.hpp b/audio/include/NovelRT/Audio/IAudioProvider.hpp index 75fd5072a..99bb6b9f1 100644 --- a/audio/include/NovelRT/Audio/IAudioProvider.hpp +++ b/audio/include/NovelRT/Audio/IAudioProvider.hpp @@ -15,7 +15,7 @@ namespace NovelRT::Audio virtual uint32_t OpenSource(AudioSourceContext& context) = 0; public: - virtual uint32_t SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, AudioSourceContext& context) = 0; + virtual uint32_t SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, AudioSourceContext& context) = 0; virtual void PlaySource(uint32_t sourceId) = 0; virtual void StopSource(uint32_t sourceId) = 0; virtual void PauseSource(uint32_t sourceId) = 0; diff --git a/include/NovelRT/ResourceManagement/AudioMetadata.h b/include/NovelRT/ResourceManagement/AudioMetadata.h index 17355fdec..ef0663cbb 100644 --- a/include/NovelRT/ResourceManagement/AudioMetadata.h +++ b/include/NovelRT/ResourceManagement/AudioMetadata.h @@ -8,7 +8,7 @@ namespace NovelRT::ResourceManagement { struct AudioMetadata { - std::vector processedAudioFrames; + std::vector processedAudioFrames; int32_t channelCount; int32_t sampleRate; uuids::uuid databaseHandle; diff --git a/src/NovelRT/CMakeLists.txt b/src/NovelRT/CMakeLists.txt index 766d91baa..756dab9fe 100644 --- a/src/NovelRT/CMakeLists.txt +++ b/src/NovelRT/CMakeLists.txt @@ -69,6 +69,7 @@ target_link_libraries(Engine runtime tbb sndfile + samplerate glfw png spdlog diff --git a/src/NovelRT/Ecs/Audio/AudioSystem.cpp b/src/NovelRT/Ecs/Audio/AudioSystem.cpp index 156489f13..38c7ce61c 100644 --- a/src/NovelRT/Ecs/Audio/AudioSystem.cpp +++ b/src/NovelRT/Ecs/Audio/AudioSystem.cpp @@ -31,6 +31,7 @@ namespace NovelRT::Ecs::Audio { case AudioEmitterState::ToPlay: { + _mixer->SetSourceLoop(emitter.handle, emitter.numberOfLoops > 0); _mixer->PlaySource(emitter.handle); _logger.logDebug("Entity ID {} - EmitterState ToPlay -> Playing", entity); states.PushComponentUpdateInstruction( @@ -216,7 +217,7 @@ namespace NovelRT::Ecs::Audio } auto asset = _resourceManagerPluginProvider->GetResourceLoader()->LoadAudioFrameData(fileName); - auto handle = _mixer->SubmitAudioBuffer(NovelRT::Utilities::Misc::Span(asset.processedAudioFrames.data(), asset.processedAudioFrames.size()), asset.channelCount, asset.sampleRate); + auto handle = _mixer->SubmitAudioBuffer(NovelRT::Utilities::Misc::Span(asset.processedAudioFrames.data(), asset.processedAudioFrames.size()), asset.channelCount, asset.sampleRate); _soundCache.emplace(handle, asset); // if (_mixer->IsLoaded(handle)) // { diff --git a/src/NovelRT/ResourceManagement/Desktop/DesktopResourceLoader.cpp b/src/NovelRT/ResourceManagement/Desktop/DesktopResourceLoader.cpp index 6bc2cab1c..119759b1f 100644 --- a/src/NovelRT/ResourceManagement/Desktop/DesktopResourceLoader.cpp +++ b/src/NovelRT/ResourceManagement/Desktop/DesktopResourceLoader.cpp @@ -3,6 +3,7 @@ #include #include +#include namespace NovelRT::ResourceManagement::Desktop { @@ -389,6 +390,7 @@ namespace NovelRT::ResourceManagement::Desktop AudioMetadata DesktopResourceLoader::LoadAudioFrameData(std::filesystem::path filePath) { constexpr size_t _bufferSize = 2048; + constexpr int32_t _sampleRate = 44100; if (filePath.is_relative()) { @@ -405,24 +407,48 @@ namespace NovelRT::ResourceManagement::Desktop throw NovelRT::Exceptions::IOException(filePath.string(), std::string(sf_strerror(file))); } - std::vector data; - std::vector readBuffer; + std::vector data; + std::vector readBuffer; readBuffer.resize(_bufferSize); - sf_command(file, SFC_SET_SCALE_FLOAT_INT_READ, nullptr, SF_TRUE); + //sf_command(file, SFC_SET_SCALE, nullptr, SF_TRUE); sf_count_t readSize = 0; - while ((readSize = sf_read_short(file, readBuffer.data(), static_cast(readBuffer.size()))) != 0) + while ((readSize = sf_read_float(file, readBuffer.data(), static_cast(readBuffer.size()))) != 0) { data.insert(data.end(), readBuffer.begin(), readBuffer.begin() + readSize); } sf_close(file); - + auto relativePathForAssetDatabase = std::filesystem::relative(filePath, _resourcesRootDirectory); uuids::uuid databaseHandle = RegisterAsset(relativePathForAssetDatabase); + if(info.samplerate != _sampleRate) + { + _logger.logDebug("Detected sample rate of {0}", info.samplerate); + info.samplerate > 44100 ? _logger.logDebug("Downscaling...") : _logger.logDebug("Upscaling..."); + std::vector resampledData = std::vector(data.size()); + SRC_DATA conversionInfo = SRC_DATA{}; + conversionInfo.data_in = data.data(); + conversionInfo.data_out = resampledData.data(); + conversionInfo.input_frames = info.channels == 1 ? data.size() : data.size() / info.channels; + conversionInfo.output_frames = conversionInfo.input_frames; + double rate = 44100.0 / static_cast(info.samplerate); + _logger.logDebug("Scaling by ratio of {0:f}", rate); + conversionInfo.src_ratio = rate; + int result = src_simple(&conversionInfo, SRC_SINC_MEDIUM_QUALITY, info.channels); + if(result != 0) + { + std::string err = src_strerror(result); + _logger.logErrorLine(err); + throw new NovelRT::Exceptions::InvalidOperationException(err); + } + + return AudioMetadata{resampledData, info.channels, _sampleRate, databaseHandle}; + } + return AudioMetadata{data, info.channels, info.samplerate, databaseHandle}; } diff --git a/thirdparty/CMakeLists.txt b/thirdparty/CMakeLists.txt index edae415b1..a15a6f55b 100644 --- a/thirdparty/CMakeLists.txt +++ b/thirdparty/CMakeLists.txt @@ -70,6 +70,12 @@ thirdparty_module(PNG URL_HASH SHA512=65df0c2befa06d0b9ec0c6395a73987fb071754610d328b94ce01a6b8f7161c71ce97ddf0e0a8e28c2e50019a103368c4b4f0b63bd8cd75e168b32f940922d96 OVERRIDE_FIND_PACKAGE ) + +thirdparty_module(samplerate + URL https://github.com/libsndfile/libsamplerate/archive/refs/tags/0.2.2.zip + URL_HASH SHA512=dedd072bc83ccebfe1a33e8a3fb3f72559bfd36282b958b1d5463b786f2325f684137580db38d91f0f4f5345a1da3e9e1c4a1695e303182e57d542c085db829f +) + thirdparty_module(SndFile URL https://github.com/novelrt/libsndfile/archive/f76d63813c810c2ed09bdb3821bb807498a7558e.zip URL_HASH SHA512=7e650af94068277246e4ccaf3b5dc20d0f93d2a2e0ecdf0f24f0be79196f879c21ec692ad48d39454f22dd01d9a4864d21458daa8d7b8f5ea4568c9551b345c1 @@ -110,7 +116,8 @@ foreach(module glm GSL GTest - Opus Ogg FLAC Vorbis SndFile OpenAL + Opus Ogg FLAC Vorbis SndFile samplerate + OpenAL ZLIB PNG fmt spdlog stduuid diff --git a/thirdparty/samplerate/CMakeLists.txt b/thirdparty/samplerate/CMakeLists.txt new file mode 100644 index 000000000..203ae7f5d --- /dev/null +++ b/thirdparty/samplerate/CMakeLists.txt @@ -0,0 +1,8 @@ +include(FetchContent) + +set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) + +set(BUILD_SHARED_LIBS ON) +set(LIBSAMPLERATE_INSTALL ON) + +FetchContent_MakeAvailable(samplerate) From 55cf57393d3616f7016a4a3cfa334a5b6bd1f649 Mon Sep 17 00:00:00 2001 From: Ken Johnson Date: Sun, 14 Apr 2024 06:57:26 -0400 Subject: [PATCH 23/40] [IN PROGRESS COMMIT] Save audio changes --- audio/include/NovelRT/Audio/XAudio2/XAudio2AudioProvider.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/audio/include/NovelRT/Audio/XAudio2/XAudio2AudioProvider.hpp b/audio/include/NovelRT/Audio/XAudio2/XAudio2AudioProvider.hpp index 0cf8c4c46..dc38f2e6f 100644 --- a/audio/include/NovelRT/Audio/XAudio2/XAudio2AudioProvider.hpp +++ b/audio/include/NovelRT/Audio/XAudio2/XAudio2AudioProvider.hpp @@ -34,7 +34,7 @@ namespace NovelRT::Audio::XAudio2 void StopSource(uint32_t sourceId) final; void PauseSource(uint32_t sourceId) final; void SetSourceProperties(uint32_t sourceId, AudioSourceContext& context) final; - uint32_t SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, AudioSourceContext& context) final; + uint32_t SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, AudioSourceContext& context) final; AudioSourceState GetSourceState(uint32_t id) final; ~XAudio2AudioProvider() final; From e3220de3d3efdf8f78b178e89df01dc732067197 Mon Sep 17 00:00:00 2001 From: Ken Johnson Date: Tue, 2 Jul 2024 16:42:35 -0400 Subject: [PATCH 24/40] Fix XAudio2 to work with floating point audio --- audio/XAudio2/XAudio2AudioProvider.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/audio/XAudio2/XAudio2AudioProvider.cpp b/audio/XAudio2/XAudio2AudioProvider.cpp index 33e78c6ba..a56570e6c 100644 --- a/audio/XAudio2/XAudio2AudioProvider.cpp +++ b/audio/XAudio2/XAudio2AudioProvider.cpp @@ -67,14 +67,14 @@ namespace NovelRT::Audio::XAudio2 { uint32_t nextSource = ++_sourceCounter; WAVEFORMATEX waveFormatContainer{}; - waveFormatContainer.wFormatTag = WAVE_FORMAT_PCM; + waveFormatContainer.wFormatTag = WAVE_FORMAT_IEEE_FLOAT; + waveFormatContainer.cbSize = 0; waveFormatContainer.nChannels = 2; + waveFormatContainer.wBitsPerSample = 32; + waveFormatContainer.nBlockAlign = 8; waveFormatContainer.nSamplesPerSec = static_cast(context.SampleRate); - waveFormatContainer.nAvgBytesPerSec = static_cast(context.SampleRate / 0.25); - waveFormatContainer.nBlockAlign = 4; - waveFormatContainer.wBitsPerSample = 16; - waveFormatContainer.cbSize = 0; - + waveFormatContainer.nAvgBytesPerSec = static_cast(waveFormatContainer.nSamplesPerSec * waveFormatContainer.nBlockAlign); + IXAudio2SourceVoice* newVoice; if(FAILED(_hr = _device->CreateSourceVoice(&newVoice, &waveFormatContainer))) @@ -108,19 +108,19 @@ namespace NovelRT::Audio::XAudio2 StopSource(sourceId); } - uint32_t XAudio2AudioProvider::SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, AudioSourceContext& context) + uint32_t XAudio2AudioProvider::SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, AudioSourceContext& context) { //uint32_t nextBuffer = ++_bufferCounter; XAUDIO2_BUFFER xABuffer = { XAUDIO2_END_OF_STREAM, // Flags - static_cast(buffer.size()*sizeof(int16_t)), // AudioBytes - static_cast(new byte[buffer.size()*sizeof(int16_t)]) //new buffer to copy int16_t* to + static_cast(buffer.size()*sizeof(float)), // AudioBytes + static_cast(new byte[buffer.size()*sizeof(float)]) //new buffer to copy float* to }; //Because XAudio2 expects a BYTE*, we'll have to cast it up and copy the data from the provided span :( std::memcpy((void*)(xABuffer.pAudioData), - reinterpret_cast(buffer.data()), buffer.size()*sizeof(int16_t)); + reinterpret_cast(buffer.data()), buffer.size()*sizeof(float)); if(context.Loop) { xABuffer.LoopCount = XAUDIO2_LOOP_INFINITE; From f3cd3de1f1aa983844f84d63fc7d299916bf4a55 Mon Sep 17 00:00:00 2001 From: kennybrew Date: Tue, 2 Jul 2024 16:44:49 -0400 Subject: [PATCH 25/40] Move OpenAL to supporting floating audio - needs bugfix for unsupported ops --- audio/OpenAL/OpenALAudioProvider.cpp | 17 +++++++++-------- .../Audio/OpenAL/OpenALAudioProvider.hpp | 8 ++++---- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/audio/OpenAL/OpenALAudioProvider.cpp b/audio/OpenAL/OpenALAudioProvider.cpp index be00e4c8b..44842a168 100644 --- a/audio/OpenAL/OpenALAudioProvider.cpp +++ b/audio/OpenAL/OpenALAudioProvider.cpp @@ -1,5 +1,6 @@ // Copyright © Matt Jones and Contributors. Licensed under the MIT Licence (MIT). See LICENCE.md in the repository root // for more information. +#include #include #include #include @@ -10,8 +11,8 @@ namespace NovelRT::Audio::OpenAL typedef void (ALC_APIENTRY*CallbackProvider)(LoggingCallback, void*) noexcept; OpenALAudioProvider::OpenALAudioProvider(): - _buffers(std::vector()), _sources(std::vector()), + _buffers(std::vector()), _logger(spdlog::stdout_color_mt("OpenAL")) { //Logger init @@ -148,18 +149,18 @@ namespace NovelRT::Audio::OpenAL default: { _logger->error("Unknown OpenAL Error - Code: {err}", err); - return std::string("Unknown OpenAL Error - Code: " + err); + return std::string("Unknown OpenAL Error - Code: " + std::to_string(err)); } } } - uint32_t OpenALAudioProvider::SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, AudioSourceContext& context) + uint32_t OpenALAudioProvider::SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, AudioSourceContext& context) { ALuint alBuffer; alGetError(); alGenBuffers(1, &alBuffer); GetALError(); - alBufferData(alBuffer, DetermineChannelFormat(context.Channels), buffer.data(), static_cast(buffer.size() * sizeof(int16_t)), context.SampleRate); + alBufferData(alBuffer, DetermineChannelFormat(context.Channels), buffer.data(), static_cast(buffer.size() * sizeof(float)), context.SampleRate); GetALError(); _buffers.emplace_back(static_cast(alBuffer)); uint32_t sourceId = OpenSource(context); @@ -214,14 +215,14 @@ namespace NovelRT::Audio::OpenAL switch(numberOfChannels) { case 1: - return AL_FORMAT_MONO16; + return AL_FORMAT_MONO_FLOAT32; case 5: - return AL_FORMAT_51CHN16; + return AL_FORMAT_51CHN32; case 7: - return AL_FORMAT_71CHN16; + return AL_FORMAT_71CHN32; case 2: default: - return AL_FORMAT_STEREO16; + return AL_FORMAT_STEREO_FLOAT32; } } diff --git a/audio/include/NovelRT/Audio/OpenAL/OpenALAudioProvider.hpp b/audio/include/NovelRT/Audio/OpenAL/OpenALAudioProvider.hpp index 61f62720a..399cbcb42 100644 --- a/audio/include/NovelRT/Audio/OpenAL/OpenALAudioProvider.hpp +++ b/audio/include/NovelRT/Audio/OpenAL/OpenALAudioProvider.hpp @@ -3,14 +3,14 @@ #pragma once #include -#include -#include +#include +#include #include #include namespace NovelRT::Audio::OpenAL { - class OpenALAudioProvider : public IAudioProvider + class OpenALAudioProvider final : public IAudioProvider { private: ALCdevice* _device; @@ -34,7 +34,7 @@ namespace NovelRT::Audio::OpenAL void StopSource(uint32_t sourceId) final; void PauseSource(uint32_t sourceId) final; void SetSourceProperties(uint32_t sourceId, AudioSourceContext& context) final; - uint32_t SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, AudioSourceContext& context) final; + uint32_t SubmitAudioBuffer(const NovelRT::Utilities::Misc::Span buffer, AudioSourceContext& context) final; AudioSourceState GetSourceState(uint32_t id) final; ~OpenALAudioProvider() final; From b78a72c09413e8c14108e3202c3af400abb441eb Mon Sep 17 00:00:00 2001 From: kennybrew Date: Tue, 2 Jul 2024 17:01:51 -0400 Subject: [PATCH 26/40] Fix bug when destroying audio provider --- audio/OpenAL/OpenALAudioProvider.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/audio/OpenAL/OpenALAudioProvider.cpp b/audio/OpenAL/OpenALAudioProvider.cpp index 44842a168..a1f12d464 100644 --- a/audio/OpenAL/OpenALAudioProvider.cpp +++ b/audio/OpenAL/OpenALAudioProvider.cpp @@ -63,11 +63,8 @@ namespace NovelRT::Audio::OpenAL GetALError(); _buffers.clear(); alcMakeContextCurrent(NULL); - GetALError(); alcDestroyContext(_context); - GetALError(); alcCloseDevice(_device); - GetALError(); } uint32_t OpenALAudioProvider::OpenSource(AudioSourceContext& context) From d5172431d64bc855d6bbddc4783e0463b1b75495 Mon Sep 17 00:00:00 2001 From: kennybrew Date: Thu, 4 Jul 2024 03:17:37 -0400 Subject: [PATCH 27/40] Fix issue where GTest in system libs was being preffered --- thirdparty/CMakeLists.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/thirdparty/CMakeLists.txt b/thirdparty/CMakeLists.txt index a15a6f55b..4c2ee499b 100644 --- a/thirdparty/CMakeLists.txt +++ b/thirdparty/CMakeLists.txt @@ -1,6 +1,6 @@ include(FetchContent) -macro(thirdparty_module name) +macro(thirdparty_module name flag) FetchContent_Declare(${name} ${ARGN} PREFIX "${CMAKE_CURRENT_BINARY_DIR}/${name}" @@ -43,8 +43,9 @@ thirdparty_module(GSL URL_HASH SHA512=710ab8b06b24dbca2cd1a8abb2eca2282548644224519784d04b1c7b70e3639f3b3e8d8f993f7f8c3dce3fcfe9ff0f3757b8d2625b676df36c84caa6f684133e ) thirdparty_module(GTest - URL https://github.com/google/googletest/archive/refs/tags/release-1.12.1.zip - URL_HASH SHA512=1479ea2f3172c622c0ca305f5b2bc45a42941221ec0ac7865e6d6d020ec4d008d952fc64e01a4c5138d7bed4148cf75596f25bb9e9044a98bbbf5662053ea11c + URL https://github.com/google/googletest/archive/refs/tags/v1.14.0.zip + URL_HASH SHA512=16cc8f5b03b862e2937d1b926ca6dc291234ac5670050dc4fc2752ec98497330ebcd46f292317cd2bbb111ce33bbdcfd088ddf6a7fe273e2442963908732d589 + OVERRIDE_FIND_PACKAGE ) thirdparty_module(nlohmann_json URL https://github.com/nlohmann/json/releases/download/v3.10.5/json.tar.xz @@ -111,7 +112,6 @@ thirdparty_module(ZLIB foreach(module nlohmann_json Fabulist - fmt glfw3 glm GSL From 6c6147b7f3ca4c5815228f6e9bd7732fa1aff98b Mon Sep 17 00:00:00 2001 From: kennybrew Date: Thu, 4 Jul 2024 03:24:04 -0400 Subject: [PATCH 28/40] Update nlohmann json --- thirdparty/CMakeLists.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/thirdparty/CMakeLists.txt b/thirdparty/CMakeLists.txt index 4c2ee499b..e6e862273 100644 --- a/thirdparty/CMakeLists.txt +++ b/thirdparty/CMakeLists.txt @@ -48,8 +48,9 @@ thirdparty_module(GTest OVERRIDE_FIND_PACKAGE ) thirdparty_module(nlohmann_json - URL https://github.com/nlohmann/json/releases/download/v3.10.5/json.tar.xz - URL_HASH SHA512=feb7dbdd1f1b6318131821f55d1411f0435262601c65eca368ccf3418750fd6348a37a3cd9d0694b31ccacfc91d02fbbecf94af52429005f8898490a5233c37d + URL https://github.com/nlohmann/json/releases/download/v3.11.3/json.tar.xz + URL_HASH SHA512=1aa94cdc3378a1fe0e3048ee73293e34bfa5ed9b46c6db0993e58e289ef818f7b7a472c0dc9c920114312e2e3ae1ff346ca797407ff48143744592adfd0a41ad + OVERRIDE_FIND_PACKAGE ) thirdparty_module(Ogg URL https://github.com/xiph/ogg/releases/download/v1.3.5/libogg-1.3.5.tar.gz From c5c353f98ad4e68099753dc99e05b42ea1928fb2 Mon Sep 17 00:00:00 2001 From: kennybrew Date: Thu, 4 Jul 2024 03:25:55 -0400 Subject: [PATCH 29/40] Remove testing mistake in thirdparty --- thirdparty/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/thirdparty/CMakeLists.txt b/thirdparty/CMakeLists.txt index e6e862273..82b9c4241 100644 --- a/thirdparty/CMakeLists.txt +++ b/thirdparty/CMakeLists.txt @@ -1,6 +1,6 @@ include(FetchContent) -macro(thirdparty_module name flag) +macro(thirdparty_module name) FetchContent_Declare(${name} ${ARGN} PREFIX "${CMAKE_CURRENT_BINARY_DIR}/${name}" From 6307007ce85430d750e41219d97d215eea1c3c7d Mon Sep 17 00:00:00 2001 From: kennybrew Date: Thu, 4 Jul 2024 03:28:17 -0400 Subject: [PATCH 30/40] Clang-format --- include/NovelRT/Ecs/Audio/Ecs.Audio.h | 4 ++-- src/NovelRT/Ecs/Audio/AudioSystem.cpp | 6 ++++-- .../ResourceManagement/Desktop/DesktopResourceLoader.cpp | 8 ++++---- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/include/NovelRT/Ecs/Audio/Ecs.Audio.h b/include/NovelRT/Ecs/Audio/Ecs.Audio.h index 96b621178..ed24e4167 100644 --- a/include/NovelRT/Ecs/Audio/Ecs.Audio.h +++ b/include/NovelRT/Ecs/Audio/Ecs.Audio.h @@ -8,10 +8,10 @@ #error NovelRT does not support including types explicitly by default. Please include Ecs.h instead for the Ecs namespace subset. #endif -#include -#include #include #include +#include +#include #include #include diff --git a/src/NovelRT/Ecs/Audio/AudioSystem.cpp b/src/NovelRT/Ecs/Audio/AudioSystem.cpp index 38c7ce61c..f14ff1aee 100644 --- a/src/NovelRT/Ecs/Audio/AudioSystem.cpp +++ b/src/NovelRT/Ecs/Audio/AudioSystem.cpp @@ -190,7 +190,7 @@ namespace NovelRT::Ecs::Audio { _mixer->SetSourceVolume(emitter.handle, emitter.volume); _logger.logDebug("Entity ID {} - Emitter Volume {} -> {}", entity, soundContext.Volume, - emitter.volume); + emitter.volume); } if (_mixer->GetSourceState(emitter.handle) != NovelRT::Audio::AudioSourceState::SOURCE_PLAYING) { @@ -217,7 +217,9 @@ namespace NovelRT::Ecs::Audio } auto asset = _resourceManagerPluginProvider->GetResourceLoader()->LoadAudioFrameData(fileName); - auto handle = _mixer->SubmitAudioBuffer(NovelRT::Utilities::Misc::Span(asset.processedAudioFrames.data(), asset.processedAudioFrames.size()), asset.channelCount, asset.sampleRate); + auto handle = _mixer->SubmitAudioBuffer( + NovelRT::Utilities::Misc::Span(asset.processedAudioFrames.data(), asset.processedAudioFrames.size()), + asset.channelCount, asset.sampleRate); _soundCache.emplace(handle, asset); // if (_mixer->IsLoaded(handle)) // { diff --git a/src/NovelRT/ResourceManagement/Desktop/DesktopResourceLoader.cpp b/src/NovelRT/ResourceManagement/Desktop/DesktopResourceLoader.cpp index 119759b1f..79172ca74 100644 --- a/src/NovelRT/ResourceManagement/Desktop/DesktopResourceLoader.cpp +++ b/src/NovelRT/ResourceManagement/Desktop/DesktopResourceLoader.cpp @@ -411,7 +411,7 @@ namespace NovelRT::ResourceManagement::Desktop std::vector readBuffer; readBuffer.resize(_bufferSize); - //sf_command(file, SFC_SET_SCALE, nullptr, SF_TRUE); + // sf_command(file, SFC_SET_SCALE, nullptr, SF_TRUE); sf_count_t readSize = 0; @@ -421,11 +421,11 @@ namespace NovelRT::ResourceManagement::Desktop } sf_close(file); - + auto relativePathForAssetDatabase = std::filesystem::relative(filePath, _resourcesRootDirectory); uuids::uuid databaseHandle = RegisterAsset(relativePathForAssetDatabase); - if(info.samplerate != _sampleRate) + if (info.samplerate != _sampleRate) { _logger.logDebug("Detected sample rate of {0}", info.samplerate); info.samplerate > 44100 ? _logger.logDebug("Downscaling...") : _logger.logDebug("Upscaling..."); @@ -439,7 +439,7 @@ namespace NovelRT::ResourceManagement::Desktop _logger.logDebug("Scaling by ratio of {0:f}", rate); conversionInfo.src_ratio = rate; int result = src_simple(&conversionInfo, SRC_SINC_MEDIUM_QUALITY, info.channels); - if(result != 0) + if (result != 0) { std::string err = src_strerror(result); _logger.logErrorLine(err); From f7d488196a0989247b7f56c4b2f3718aee9f40d2 Mon Sep 17 00:00:00 2001 From: kennybrew Date: Thu, 4 Jul 2024 04:37:01 -0400 Subject: [PATCH 31/40] Remove NrtAudioService, and interop references to legacy audio system --- include/NovelRT.Interop/Audio/NrtAudio.h | 9 - .../NovelRT.Interop/Audio/NrtAudioService.h | 55 --- .../NovelRT.Interop/Audio/NrtAudioTypedefs.h | 19 - include/NovelRT.Interop/NrtTypedefs.h | 1 - src/NovelRT.Interop/Audio/NrtAudioService.cpp | 413 ------------------ src/NovelRT.Interop/CMakeLists.txt | 6 - .../Ecs/Audio/NrtAudioSystem.cpp | 1 - 7 files changed, 504 deletions(-) delete mode 100644 include/NovelRT.Interop/Audio/NrtAudio.h delete mode 100644 include/NovelRT.Interop/Audio/NrtAudioService.h delete mode 100644 include/NovelRT.Interop/Audio/NrtAudioTypedefs.h delete mode 100644 src/NovelRT.Interop/Audio/NrtAudioService.cpp diff --git a/include/NovelRT.Interop/Audio/NrtAudio.h b/include/NovelRT.Interop/Audio/NrtAudio.h deleted file mode 100644 index 28908c911..000000000 --- a/include/NovelRT.Interop/Audio/NrtAudio.h +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright © Matt Jones and Contributors. Licensed under the MIT Licence (MIT). See LICENCE.md in the repository root -// for more information. - -#ifndef NOVELRT_INTEROP_AUDIO_H -#define NOVELRT_INTEROP_AUDIO_H - -#include "NrtAudioService.h" - -#endif // NOVELRT_INTEROP_AUDIO_H diff --git a/include/NovelRT.Interop/Audio/NrtAudioService.h b/include/NovelRT.Interop/Audio/NrtAudioService.h deleted file mode 100644 index eb1c64b93..000000000 --- a/include/NovelRT.Interop/Audio/NrtAudioService.h +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright © Matt Jones and Contributors. Licensed under the MIT Licence (MIT). See LICENCE.md in the repository root -// for more information. - -#ifndef NOVELRT_INTEROP_AUDIO_AUDIOSERVICE_H -#define NOVELRT_INTEROP_AUDIO_AUDIOSERVICE_H - -#include "../NrtTypedefs.h" - -#ifdef __cplusplus -extern "C" -{ -#endif - - NrtAudioServiceHandle Nrt_AudioService_Create(); - NrtResult Nrt_AudioService_Destroy(NrtAudioServiceHandle service); - NrtBool Nrt_AudioService_InitialiseAudio(NrtAudioServiceHandle service); - NrtResult Nrt_AudioService_LoadMusic(NrtAudioServiceHandle service, - NrtInt16VectorHandle audioFrameData, - int32_t channelCount, - int32_t sampleRate, - NrtAudioServiceIteratorHandle* output); - NrtResult Nrt_AudioService_SetSoundVolume(NrtAudioServiceHandle service, unsigned int source, float val); - NrtResult Nrt_AudioService_SetSoundPosition(NrtAudioServiceHandle service, - unsigned int source, - float posX, - float posY); - NrtResult Nrt_AudioService_ResumeMusic(NrtAudioServiceHandle service); - NrtResult Nrt_AudioService_PlayMusic(NrtAudioServiceHandle service, - NrtAudioServiceIteratorHandle handle, - int32_t loops); - NrtResult Nrt_AudioService_PauseMusic(NrtAudioServiceHandle service); - NrtResult Nrt_AudioService_StopMusic(NrtAudioServiceHandle service); - NrtResult Nrt_AudioService_SetMusicVolume(NrtAudioServiceHandle service, float value); - NrtResult Nrt_AudioService_CheckSources(NrtAudioServiceHandle service); - NrtResult Nrt_AudioService_LoadSound(NrtAudioServiceHandle service, - NrtInt16VectorHandle audioFrameData, - int32_t channelCount, - int32_t sampleRate, - uint32_t* output); - NrtResult Nrt_AudioService_Unload(NrtAudioServiceHandle service, unsigned int handle); - NrtResult Nrt_AudioService_PlaySound(NrtAudioServiceHandle service, unsigned int handle, int loops); - NrtResult Nrt_AudioService_StopSound(NrtAudioServiceHandle service, unsigned int handle); - NrtResult Nrt_AudioService_TearDown(NrtAudioServiceHandle service); - NrtBool Nrt_AudioService_IsMusicLoaded(NrtAudioServiceHandle service, NrtAudioServiceIteratorHandle handle); - NrtBool Nrt_AudioService_IsSoundLoaded(NrtAudioServiceHandle service, unsigned int handle); - NrtBool Nrt_AudioService_IsMusicPlaying(NrtAudioServiceHandle service); - NrtBool Nrt_AudioService_IsSoundPlaying(NrtAudioServiceHandle service, unsigned int handle); - float Nrt_AudioService_GetMusicVolume(NrtAudioServiceHandle service); - float Nrt_AudioService_GetSoundVolume(NrtAudioServiceHandle service, unsigned int source); - -#ifdef __cplusplus -} -#endif - -#endif // NOVELRT_INTEROP_AUDIO_AUDIOSERVICE_H diff --git a/include/NovelRT.Interop/Audio/NrtAudioTypedefs.h b/include/NovelRT.Interop/Audio/NrtAudioTypedefs.h deleted file mode 100644 index 1323c8d03..000000000 --- a/include/NovelRT.Interop/Audio/NrtAudioTypedefs.h +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright © Matt Jones and Contributors. Licensed under the MIT License (MIT). See LICENCE.md in the repository root -// for more information. - -#ifndef NOVELRT_INTEROP_AUDIO_AUDIOTYPEDEFS_H -#define NOVELRT_INTEROP_AUDIO_AUDIOTYPEDEFS_H - -#ifdef __cplusplus -extern "C" -{ -#endif - - typedef struct NrtAudioService* NrtAudioServiceHandle; - typedef struct NrtAudioServiceIterator* NrtAudioServiceIteratorHandle; - -#ifdef __cplusplus -} -#endif - -#endif // NOVELRT_INTEROP_AUDIO_AUDIOTYPEDEFS_H diff --git a/include/NovelRT.Interop/NrtTypedefs.h b/include/NovelRT.Interop/NrtTypedefs.h index ce4ed8062..495216266 100644 --- a/include/NovelRT.Interop/NrtTypedefs.h +++ b/include/NovelRT.Interop/NrtTypedefs.h @@ -68,7 +68,6 @@ extern "C" #include "Maths/NrtMathsTypedefs.h" #include "Timing/NrtTimingTypedefs.h" #include "Utilities/NrtUtilitiesTypedefs.h" -#include "Audio/NrtAudioTypedefs.h" #include "Ecs/Audio/NrtEcsAudioTypedefs.h" #include "Ecs/Graphics/NrtEcsGraphicsTypedefs.h" #include "Ecs/NrtEcsTypedefs.h" diff --git a/src/NovelRT.Interop/Audio/NrtAudioService.cpp b/src/NovelRT.Interop/Audio/NrtAudioService.cpp deleted file mode 100644 index 36cd7321a..000000000 --- a/src/NovelRT.Interop/Audio/NrtAudioService.cpp +++ /dev/null @@ -1,413 +0,0 @@ -// Copyright © Matt Jones and Contributors. Licensed under the MIT Licence (MIT). See LICENCE.md in the repository root -// for more information. - -#include -#include -#include - -#ifdef __cplusplus -using namespace NovelRT; - -extern "C" -{ -#endif - - NrtAudioServiceHandle Nrt_AudioService_Create() - { - return reinterpret_cast(new Audio::AudioService()); - } - - NrtResult Nrt_AudioService_Destroy(NrtAudioServiceHandle service) - { - if (service == nullptr) - { - Nrt_setErrMsgIsNullInstanceProvidedInternal(); - return NRT_FAILURE_NULL_INSTANCE_PROVIDED; - } - - auto serv = reinterpret_cast(service); - serv->~AudioService(); - return NRT_SUCCESS; - } - - NrtBool Nrt_AudioService_InitialiseAudio(NrtAudioServiceHandle service) - { - auto serv = reinterpret_cast(service); - - try - { - return serv->InitializeAudio() ? NRT_TRUE : NRT_FALSE; - } - catch (const Exceptions::InitialisationFailureException&) - { - Nrt_setErrMsgIsInitialisationFailureInternal(); - return NRT_FAILURE_INITIALISATION_FAILURE; - } - } - - NrtResult Nrt_AudioService_LoadMusic(NrtAudioServiceHandle service, - NrtInt16VectorHandle audioFrameData, - int32_t channelCount, - int32_t sampleRate, - NrtAudioServiceIteratorHandle* output) - { - if (service == nullptr) - { - Nrt_setErrMsgIsNullInstanceProvidedInternal(); - return NRT_FAILURE_NULL_INSTANCE_PROVIDED; - } - - auto serv = reinterpret_cast(service); - - NovelRT::Audio::SoundBank::iterator out; - try - { - out = serv->LoadMusic(*reinterpret_cast*>(audioFrameData), channelCount, sampleRate); - } - catch (const Exceptions::NotInitialisedException&) - { - Nrt_setErrMsgIsNotInitialisedInternal(); - return NRT_FAILURE_NOT_INITIALISED; - } - - *output = reinterpret_cast(out); - return NRT_SUCCESS; - } - - NrtResult Nrt_AudioService_SetSoundVolume(NrtAudioServiceHandle service, unsigned int source, float val) - { - if (service == nullptr) - { - Nrt_setErrMsgIsNullInstanceProvidedInternal(); - return NRT_FAILURE_NULL_INSTANCE_PROVIDED; - } - - auto serv = reinterpret_cast(service); - - try - { - serv->SetSoundVolume(source, val); - } - catch (const Exceptions::NotInitialisedException&) - { - Nrt_setErrMsgIsNotInitialisedInternal(); - return NRT_FAILURE_NOT_INITIALISED; - } - - return NRT_SUCCESS; - } - - NrtResult Nrt_AudioService_SetSoundPosition(NrtAudioServiceHandle service, - unsigned int source, - float posX, - float posY) - { - if (service == nullptr) - { - Nrt_setErrMsgIsNullInstanceProvidedInternal(); - return NRT_FAILURE_NULL_INSTANCE_PROVIDED; - } - - auto serv = reinterpret_cast(service); - - try - { - serv->SetSoundPosition(source, posX, posY); - } - catch (const Exceptions::NotInitialisedException&) - { - Nrt_setErrMsgIsNotInitialisedInternal(); - return NRT_FAILURE_NOT_INITIALISED; - } - - return NRT_SUCCESS; - } - - NrtResult Nrt_AudioService_ResumeMusic(NrtAudioServiceHandle service) - { - if (service == nullptr) - { - Nrt_setErrMsgIsNullInstanceProvidedInternal(); - return NRT_FAILURE_NULL_INSTANCE_PROVIDED; - } - - auto serv = reinterpret_cast(service); - - try - { - serv->ResumeMusic(); - } - catch (const Exceptions::NotInitialisedException&) - { - Nrt_setErrMsgIsNotInitialisedInternal(); - return NRT_FAILURE_NOT_INITIALISED; - } - - return NRT_SUCCESS; - } - - NrtResult Nrt_AudioService_PlayMusic(NrtAudioServiceHandle service, - NrtAudioServiceIteratorHandle handle, - int32_t loops) - { - if (service == nullptr) - { - Nrt_setErrMsgIsNullInstanceProvidedInternal(); - return NRT_FAILURE_NULL_INSTANCE_PROVIDED; - } - - auto serv = reinterpret_cast(service); - - try - { - serv->PlayMusic(reinterpret_cast(handle), loops); - } - catch (const Exceptions::NotInitialisedException&) - { - Nrt_setErrMsgIsNotInitialisedInternal(); - return NRT_FAILURE_NOT_INITIALISED; - } - - return NRT_SUCCESS; - } - - NrtResult Nrt_AudioService_PauseMusic(NrtAudioServiceHandle service) - { - if (service == nullptr) - { - Nrt_setErrMsgIsNullInstanceProvidedInternal(); - return NRT_FAILURE_NULL_INSTANCE_PROVIDED; - } - - auto serv = reinterpret_cast(service); - - try - { - serv->PauseMusic(); - } - catch (const Exceptions::NotInitialisedException&) - { - Nrt_setErrMsgIsNotInitialisedInternal(); - return NRT_FAILURE_NOT_INITIALISED; - } - - return NRT_SUCCESS; - } - - NrtResult Nrt_AudioService_StopMusic(NrtAudioServiceHandle service) - { - if (service == nullptr) - { - Nrt_setErrMsgIsNullInstanceProvidedInternal(); - return NRT_FAILURE_NULL_INSTANCE_PROVIDED; - } - - auto serv = reinterpret_cast(service); - - try - { - serv->StopMusic(); - } - catch (const Exceptions::NotInitialisedException&) - { - Nrt_setErrMsgIsNotInitialisedInternal(); - return NRT_FAILURE_NOT_INITIALISED; - } - - return NRT_SUCCESS; - } - - NrtResult Nrt_AudioService_SetMusicVolume(NrtAudioServiceHandle service, float value) - { - if (service == nullptr) - { - Nrt_setErrMsgIsNullInstanceProvidedInternal(); - return NRT_FAILURE_NULL_INSTANCE_PROVIDED; - } - - auto serv = reinterpret_cast(service); - - try - { - serv->SetMusicVolume(value); - } - catch (const Exceptions::NotInitialisedException&) - { - Nrt_setErrMsgIsNotInitialisedInternal(); - return NRT_FAILURE_NOT_INITIALISED; - } - - return NRT_SUCCESS; - } - - NrtResult Nrt_AudioService_CheckSources(NrtAudioServiceHandle service) - { - if (service == nullptr) - { - Nrt_setErrMsgIsNullInstanceProvidedInternal(); - return NRT_FAILURE_NULL_INSTANCE_PROVIDED; - } - - auto serv = reinterpret_cast(service); - serv->CheckSources(); - - return NRT_SUCCESS; - } - - NrtResult Nrt_AudioService_LoadSound(NrtAudioServiceHandle service, - NrtInt16VectorHandle audioFrameData, - int32_t channelCount, - int32_t sampleRate, - uint32_t* output) - { - if (service == nullptr) - { - Nrt_setErrMsgIsNullInstanceProvidedInternal(); - return NRT_FAILURE_NULL_INSTANCE_PROVIDED; - } - - auto serv = reinterpret_cast(service); - - try - { - *output = - serv->LoadSound(*reinterpret_cast*>(audioFrameData), channelCount, sampleRate); - } - catch (const Exceptions::NotInitialisedException&) - { - Nrt_setErrMsgIsNotInitialisedInternal(); - return NRT_FAILURE_NOT_INITIALISED; - } - - return NRT_SUCCESS; - } - - NrtResult Nrt_AudioService_Unload(NrtAudioServiceHandle service, unsigned int handle) - { - if (service == nullptr) - { - Nrt_setErrMsgIsNullInstanceProvidedInternal(); - return NRT_FAILURE_NULL_INSTANCE_PROVIDED; - } - - auto serv = reinterpret_cast(service); - serv->Unload(handle); - return NRT_SUCCESS; - } - - NrtResult Nrt_AudioService_PlaySound(NrtAudioServiceHandle service, unsigned int handle, int loops) - { - if (service == nullptr) - { - Nrt_setErrMsgIsNullInstanceProvidedInternal(); - return NRT_FAILURE_NULL_INSTANCE_PROVIDED; - } - - auto serv = reinterpret_cast(service); - - try - { - serv->PlaySound(handle, loops); - } - catch (const Exceptions::NotInitialisedException&) - { - Nrt_setErrMsgIsNotInitialisedInternal(); - return NRT_FAILURE_NOT_INITIALISED; - } - - return NRT_SUCCESS; - } - - NrtResult Nrt_AudioService_StopSound(NrtAudioServiceHandle service, unsigned int handle) - { - if (service == nullptr) - { - Nrt_setErrMsgIsNullInstanceProvidedInternal(); - return NRT_FAILURE_NULL_INSTANCE_PROVIDED; - } - - auto serv = reinterpret_cast(service); - serv->StopSound(handle); - return NRT_SUCCESS; - } - - NrtResult Nrt_AudioService_TearDown(NrtAudioServiceHandle service) - { - if (service == nullptr) - { - Nrt_setErrMsgIsNullInstanceProvidedInternal(); - return NRT_FAILURE_NULL_INSTANCE_PROVIDED; - } - - auto serv = reinterpret_cast(service); - serv->TearDown(); - return NRT_SUCCESS; - } - - NrtBool Nrt_AudioService_IsMusicLoaded(NrtAudioServiceHandle service, NrtAudioServiceIteratorHandle handle) - { - if (service == nullptr) - { - Nrt_setErrMsgIsNullInstanceProvidedInternal(); - return NRT_FAILURE_NULL_INSTANCE_PROVIDED; - } - - auto serv = reinterpret_cast(service); - - return serv->IsLoaded(reinterpret_cast(handle)) ? NRT_TRUE : NRT_FALSE; - } - - NrtBool Nrt_AudioService_IsSoundLoaded(NrtAudioServiceHandle service, unsigned int handle) - { - if (service == nullptr) - { - Nrt_setErrMsgIsNullInstanceProvidedInternal(); - return NRT_FAILURE_NULL_INSTANCE_PROVIDED; - } - - auto serv = reinterpret_cast(service); - - return serv->IsLoaded(handle) ? NRT_TRUE : NRT_FALSE; - } - - NrtBool Nrt_AudioService_IsMusicPlaying(NrtAudioServiceHandle service) - { - if (service == nullptr) - { - Nrt_setErrMsgIsNullInstanceProvidedInternal(); - return NRT_FAILURE_NULL_INSTANCE_PROVIDED; - } - - auto serv = reinterpret_cast(service); - - return serv->IsMusicPlaying() ? NRT_TRUE : NRT_FALSE; - } - - NrtBool Nrt_AudioService_IsSoundPlaying(NrtAudioServiceHandle service, unsigned int handle) - { - if (service == nullptr) - { - Nrt_setErrMsgIsNullInstanceProvidedInternal(); - return NRT_FAILURE_NULL_INSTANCE_PROVIDED; - } - - auto serv = reinterpret_cast(service); - - return serv->IsSoundPlaying(handle) ? NRT_TRUE : NRT_FALSE; - } - - float Nrt_AudioService_GetMusicVolume(NrtAudioServiceHandle service) - { - auto serv = reinterpret_cast(service); - return serv->GetMusicVolume(); - } - - float Nrt_AudioService_GetSoundVolume(NrtAudioServiceHandle service, unsigned int source) - { - auto serv = reinterpret_cast(service); - return serv->GetSoundVolume(source); - } - -#ifdef __cplusplus -} -#endif diff --git a/src/NovelRT.Interop/CMakeLists.txt b/src/NovelRT.Interop/CMakeLists.txt index 96ee69762..b7eda86b2 100644 --- a/src/NovelRT.Interop/CMakeLists.txt +++ b/src/NovelRT.Interop/CMakeLists.txt @@ -2,8 +2,6 @@ set(INTEROP_SOURCES NrtInteropErrorHandling.cpp NrtLoggingService.cpp - Audio/NrtAudioService.cpp - Ecs/NrtCatalogue.cpp Ecs/NrtConfigurator.cpp Ecs/NrtComponentBufferMemoryContainer.cpp @@ -14,10 +12,6 @@ set(INTEROP_SOURCES Ecs/NrtSystemScheduler.cpp Ecs/NrtUnsafeComponentView.cpp - Ecs/Audio/NrtAudioSystem.cpp - Ecs/Audio/NrtAudioEmitterComponent.cpp - Ecs/Audio/NrtAudioEmitterStateComponent.cpp - Ecs/Graphics/NrtDefaultRenderingSystem.cpp Graphics/NrtRGBAColour.cpp diff --git a/src/NovelRT.Interop/Ecs/Audio/NrtAudioSystem.cpp b/src/NovelRT.Interop/Ecs/Audio/NrtAudioSystem.cpp index 0f2ff1408..a0e125ab4 100644 --- a/src/NovelRT.Interop/Ecs/Audio/NrtAudioSystem.cpp +++ b/src/NovelRT.Interop/Ecs/Audio/NrtAudioSystem.cpp @@ -1,7 +1,6 @@ // Copyright © Matt Jones and Contributors. Licensed under the MIT Licence (MIT). See LICENCE.md in the repository root // for more information. -#include #include #include From ca7a6650e130ddfde12f2813797d2f254fa55df3 Mon Sep 17 00:00:00 2001 From: kennybrew Date: Thu, 4 Jul 2024 04:46:31 -0400 Subject: [PATCH 32/40] Fix interop building issues for clang --- include/NovelRT/Utilities/Event.h | 1 + src/NovelRT.Interop/CMakeLists.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/include/NovelRT/Utilities/Event.h b/include/NovelRT/Utilities/Event.h index 045ec5734..eb009e5a9 100644 --- a/include/NovelRT/Utilities/Event.h +++ b/include/NovelRT/Utilities/Event.h @@ -4,6 +4,7 @@ #include "../Atom.h" #include #include +#include #ifndef NOVELRT_UTILITIES_EVENT_H #define NOVELRT_UTILITIES_EVENT_H diff --git a/src/NovelRT.Interop/CMakeLists.txt b/src/NovelRT.Interop/CMakeLists.txt index b7eda86b2..952e99aab 100644 --- a/src/NovelRT.Interop/CMakeLists.txt +++ b/src/NovelRT.Interop/CMakeLists.txt @@ -125,6 +125,7 @@ target_compile_options(Interop $<$:-Wno-padded> $<$:-Wno-reserved-id-macro> $<$:-Wno-nullability-extension> + $<$:-Wno-unused-but-set-variable> $<$:-pedantic> $<$:-pedantic-errors> From 1c5155ea3f1dd5484cd1f37ae8e4ebb01e19bc1d Mon Sep 17 00:00:00 2001 From: kennybrew Date: Thu, 4 Jul 2024 04:46:52 -0400 Subject: [PATCH 33/40] Pin OpenAL to new version that fixes compiler issues with internal function --- thirdparty/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/thirdparty/CMakeLists.txt b/thirdparty/CMakeLists.txt index 82b9c4241..40295501c 100644 --- a/thirdparty/CMakeLists.txt +++ b/thirdparty/CMakeLists.txt @@ -58,8 +58,8 @@ thirdparty_module(Ogg OVERRIDE_FIND_PACKAGE ) thirdparty_module(OpenAL - URL https://github.com/kcat/openal-soft/archive/c03603b58d4cf6a25d36bca00305970bc9f163b4.zip - URL_HASH SHA512=331bf7d7ee138e3059dcdff68de153f73ba9b30dd098a0f5913d1e51d7292b8ad6f0e8642cb125bb8a757e70660a20375e5e76b70ddc59e5cbaab25676cbf9b8 + URL https://github.com/kcat/openal-soft/archive/ef13491d15b8a8eb196cd1f674597ba72e14a2a8.zip + URL_HASH SHA512=167e4939fec7c2a67dd39630d6a325389a398ab5ecf2ba44be50f68dc98577696d5891c46543a9077176e39dda35232cdeddc190a84a2f51a7d58acdbcd050c4 OVERRIDE_FIND_PACKAGE ) thirdparty_module(Opus From 92afb65e75643afe200bd7b69e68209ea67b87e1 Mon Sep 17 00:00:00 2001 From: kennybrew Date: Sun, 7 Jul 2024 09:26:20 -0400 Subject: [PATCH 34/40] Revert OpenAL-Soft to not as new version for clang 10 compat --- thirdparty/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/thirdparty/CMakeLists.txt b/thirdparty/CMakeLists.txt index 40295501c..a3261deea 100644 --- a/thirdparty/CMakeLists.txt +++ b/thirdparty/CMakeLists.txt @@ -58,8 +58,8 @@ thirdparty_module(Ogg OVERRIDE_FIND_PACKAGE ) thirdparty_module(OpenAL - URL https://github.com/kcat/openal-soft/archive/ef13491d15b8a8eb196cd1f674597ba72e14a2a8.zip - URL_HASH SHA512=167e4939fec7c2a67dd39630d6a325389a398ab5ecf2ba44be50f68dc98577696d5891c46543a9077176e39dda35232cdeddc190a84a2f51a7d58acdbcd050c4 + URL https://github.com/kcat/openal-soft/archive/a0e238f54dbf57a2c88f2b45a0c49f6a13e0784c.zip + URL_HASH SHA512=bfe50a959b2e1d6edf78c282839f52b8f596d58b9e785d6948e4a9edc8629421f09df542dc38bc60a987b58c38de9f5be9377f731516a05c3cb02d9f883ae629 OVERRIDE_FIND_PACKAGE ) thirdparty_module(Opus From daa87680ab6e8e87743379aec69fabae9563f117 Mon Sep 17 00:00:00 2001 From: kennybrew Date: Sun, 7 Jul 2024 10:16:36 -0400 Subject: [PATCH 35/40] Clang-format and reverting OpenAL again --- include/NovelRT/Utilities/Event.h | 2 +- thirdparty/CMakeLists.txt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/NovelRT/Utilities/Event.h b/include/NovelRT/Utilities/Event.h index eb009e5a9..6c4044e04 100644 --- a/include/NovelRT/Utilities/Event.h +++ b/include/NovelRT/Utilities/Event.h @@ -2,9 +2,9 @@ // for more information. #include "../Atom.h" +#include #include #include -#include #ifndef NOVELRT_UTILITIES_EVENT_H #define NOVELRT_UTILITIES_EVENT_H diff --git a/thirdparty/CMakeLists.txt b/thirdparty/CMakeLists.txt index a3261deea..8f4203805 100644 --- a/thirdparty/CMakeLists.txt +++ b/thirdparty/CMakeLists.txt @@ -58,8 +58,8 @@ thirdparty_module(Ogg OVERRIDE_FIND_PACKAGE ) thirdparty_module(OpenAL - URL https://github.com/kcat/openal-soft/archive/a0e238f54dbf57a2c88f2b45a0c49f6a13e0784c.zip - URL_HASH SHA512=bfe50a959b2e1d6edf78c282839f52b8f596d58b9e785d6948e4a9edc8629421f09df542dc38bc60a987b58c38de9f5be9377f731516a05c3cb02d9f883ae629 + URL https://github.com/kcat/openal-soft/archive/ef44737658313cc0a6394eed7878ad28d36fdca5.zip + URL_HASH SHA512=66b1e5a373ab6f5414220b7b22c46a0e0eba7d83e004088be75bb5ec808e4d4e2152fc14989ba182475c4d1d020332bbd9b3deb1b26f8506d40112f995e02206 OVERRIDE_FIND_PACKAGE ) thirdparty_module(Opus From 55f9f4d38a64c4a79d0e110909e240ba1ef7dced Mon Sep 17 00:00:00 2001 From: kennybrew Date: Sun, 7 Jul 2024 13:47:56 -0400 Subject: [PATCH 36/40] Revert change made to support Clang 17 --- src/NovelRT.Interop/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/NovelRT.Interop/CMakeLists.txt b/src/NovelRT.Interop/CMakeLists.txt index 952e99aab..b7eda86b2 100644 --- a/src/NovelRT.Interop/CMakeLists.txt +++ b/src/NovelRT.Interop/CMakeLists.txt @@ -125,7 +125,6 @@ target_compile_options(Interop $<$:-Wno-padded> $<$:-Wno-reserved-id-macro> $<$:-Wno-nullability-extension> - $<$:-Wno-unused-but-set-variable> $<$:-pedantic> $<$:-pedantic-errors> From d85d52c7dd23b90707257d77b38ae5ce8d2da7aa Mon Sep 17 00:00:00 2001 From: Ken Johnson Date: Mon, 29 Jul 2024 08:58:45 -0400 Subject: [PATCH 37/40] Fix missing change volume function --- include/NovelRT/Ecs/Audio/AudioSystem.h | 2 +- src/NovelRT/Ecs/Audio/AudioSystem.cpp | 20 ++++---------------- 2 files changed, 5 insertions(+), 17 deletions(-) diff --git a/include/NovelRT/Ecs/Audio/AudioSystem.h b/include/NovelRT/Ecs/Audio/AudioSystem.h index cb7ab9e43..0b003f2f0 100644 --- a/include/NovelRT/Ecs/Audio/AudioSystem.h +++ b/include/NovelRT/Ecs/Audio/AudioSystem.h @@ -21,7 +21,7 @@ namespace NovelRT::Ecs::Audio NovelRT::Timing::Timestamp _systemTime; std::shared_ptr _resourceManagerPluginProvider; - // void ChangeAudioVolume(AudioEmitterComponent emitter, float desiredVolume); + void ChangeAudioVolume(AudioEmitterComponent emitter, float desiredVolume); public: AudioSystem(std::shared_ptr resourceManagerPluginProvider); diff --git a/src/NovelRT/Ecs/Audio/AudioSystem.cpp b/src/NovelRT/Ecs/Audio/AudioSystem.cpp index c9cbea82f..173781f05 100644 --- a/src/NovelRT/Ecs/Audio/AudioSystem.cpp +++ b/src/NovelRT/Ecs/Audio/AudioSystem.cpp @@ -221,26 +221,14 @@ namespace NovelRT::Ecs::Audio NovelRT::Utilities::Misc::Span(asset.processedAudioFrames.data(), asset.processedAudioFrames.size()), asset.channelCount, asset.sampleRate); _soundCache.emplace(handle, asset); - // if (_mixer->IsLoaded(handle)) - // { - // _soundCache.insert({_counter, handle}); - // value = _counter; _counter++; - // } return handle; } - // void AudioSystem::ChangeAudioVolume(AudioEmitterComponent emitter, float desiredVolume) - // { - // if (emitter.isMusic) - // { - // _mixer->SetMusicVolume(desiredVolume); - // } - // else - // { - // _mixer->SetSoundVolume(_soundCache.at(emitter.handle), desiredVolume); - // } - // } + void AudioSystem::ChangeAudioVolume(AudioEmitterComponent emitter, float desiredVolume) + { + _mixer->SetSourceVolume(emitter.handle, desiredVolume); + } AudioSystem::~AudioSystem() noexcept { From ff50ad2bbed622b07e0671a61d620aeaf1aa0af6 Mon Sep 17 00:00:00 2001 From: Ken Johnson Date: Mon, 29 Jul 2024 15:40:39 -0400 Subject: [PATCH 38/40] Fix issues caused by merge --- src/NovelRT/Ecs/Audio/AudioSystem.cpp | 2 +- .../ResourceManagement/Desktop/DesktopResourceLoader.cpp | 6 +++--- thirdparty/CMakeLists.txt | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/NovelRT/Ecs/Audio/AudioSystem.cpp b/src/NovelRT/Ecs/Audio/AudioSystem.cpp index 173781f05..47d07c7e8 100644 --- a/src/NovelRT/Ecs/Audio/AudioSystem.cpp +++ b/src/NovelRT/Ecs/Audio/AudioSystem.cpp @@ -190,7 +190,7 @@ namespace NovelRT::Ecs::Audio { _mixer->SetSourceVolume(emitter.handle, emitter.volume); _logger.logDebug("Entity ID {} - Emitter Volume {} -> {}", entity, soundContext.Volume, - emitter.volume); + emitter.volume); } if (_mixer->GetSourceState(emitter.handle) != NovelRT::Audio::AudioSourceState::SOURCE_PLAYING) { diff --git a/src/NovelRT/ResourceManagement/Desktop/DesktopResourceLoader.cpp b/src/NovelRT/ResourceManagement/Desktop/DesktopResourceLoader.cpp index cffc8c964..86a7a12ec 100644 --- a/src/NovelRT/ResourceManagement/Desktop/DesktopResourceLoader.cpp +++ b/src/NovelRT/ResourceManagement/Desktop/DesktopResourceLoader.cpp @@ -419,11 +419,11 @@ namespace NovelRT::ResourceManagement::Desktop } sf_close(file); - + auto relativePathForAssetDatabase = std::filesystem::relative(filePath, _resourcesRootDirectory); uuids::uuid databaseHandle = RegisterAsset(relativePathForAssetDatabase); - if(info.samplerate != _sampleRate) + if (info.samplerate != _sampleRate) { _logger.logDebug("Detected sample rate of {0}", info.samplerate); info.samplerate > 44100 ? _logger.logDebug("Downscaling...") : _logger.logDebug("Upscaling..."); @@ -437,7 +437,7 @@ namespace NovelRT::ResourceManagement::Desktop _logger.logDebug("Scaling by ratio of {0:f}", rate); conversionInfo.src_ratio = rate; int result = src_simple(&conversionInfo, SRC_SINC_MEDIUM_QUALITY, info.channels); - if(result != 0) + if (result != 0) { std::string err = src_strerror(result); _logger.logErrorLine(err); diff --git a/thirdparty/CMakeLists.txt b/thirdparty/CMakeLists.txt index 82b9c4241..854b91c13 100644 --- a/thirdparty/CMakeLists.txt +++ b/thirdparty/CMakeLists.txt @@ -58,8 +58,8 @@ thirdparty_module(Ogg OVERRIDE_FIND_PACKAGE ) thirdparty_module(OpenAL - URL https://github.com/kcat/openal-soft/archive/c03603b58d4cf6a25d36bca00305970bc9f163b4.zip - URL_HASH SHA512=331bf7d7ee138e3059dcdff68de153f73ba9b30dd098a0f5913d1e51d7292b8ad6f0e8642cb125bb8a757e70660a20375e5e76b70ddc59e5cbaab25676cbf9b8 + URL https://github.com/kcat/openal-soft/archive/1318bea2e0f0af9430335708e65ae2ff920d98c6.zip + URL_HASH SHA512=a561b962ecfc6135cb6f5e4d9ba7b9719b8e6dce85112ca0dd4fe3efd05d86308a4076e0cba9e628b1a4ee2f984efabe517c1aa1399809ca848bae90b449ba4f OVERRIDE_FIND_PACKAGE ) thirdparty_module(Opus From fb8aeeed377675ffeb5908b84a3df491ce1684d1 Mon Sep 17 00:00:00 2001 From: Ken Johnson Date: Mon, 29 Jul 2024 15:49:20 -0400 Subject: [PATCH 39/40] Restore clang-10 supported OpenAL --- thirdparty/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/thirdparty/CMakeLists.txt b/thirdparty/CMakeLists.txt index 854b91c13..a3261deea 100644 --- a/thirdparty/CMakeLists.txt +++ b/thirdparty/CMakeLists.txt @@ -58,8 +58,8 @@ thirdparty_module(Ogg OVERRIDE_FIND_PACKAGE ) thirdparty_module(OpenAL - URL https://github.com/kcat/openal-soft/archive/1318bea2e0f0af9430335708e65ae2ff920d98c6.zip - URL_HASH SHA512=a561b962ecfc6135cb6f5e4d9ba7b9719b8e6dce85112ca0dd4fe3efd05d86308a4076e0cba9e628b1a4ee2f984efabe517c1aa1399809ca848bae90b449ba4f + URL https://github.com/kcat/openal-soft/archive/a0e238f54dbf57a2c88f2b45a0c49f6a13e0784c.zip + URL_HASH SHA512=bfe50a959b2e1d6edf78c282839f52b8f596d58b9e785d6948e4a9edc8629421f09df542dc38bc60a987b58c38de9f5be9377f731516a05c3cb02d9f883ae629 OVERRIDE_FIND_PACKAGE ) thirdparty_module(Opus From fa7b475a6b21795e43d40f168057a92c9959b7c8 Mon Sep 17 00:00:00 2001 From: Ken Johnson Date: Mon, 29 Jul 2024 16:15:46 -0400 Subject: [PATCH 40/40] Revert openal updates --- thirdparty/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/thirdparty/CMakeLists.txt b/thirdparty/CMakeLists.txt index a3261deea..8f4203805 100644 --- a/thirdparty/CMakeLists.txt +++ b/thirdparty/CMakeLists.txt @@ -58,8 +58,8 @@ thirdparty_module(Ogg OVERRIDE_FIND_PACKAGE ) thirdparty_module(OpenAL - URL https://github.com/kcat/openal-soft/archive/a0e238f54dbf57a2c88f2b45a0c49f6a13e0784c.zip - URL_HASH SHA512=bfe50a959b2e1d6edf78c282839f52b8f596d58b9e785d6948e4a9edc8629421f09df542dc38bc60a987b58c38de9f5be9377f731516a05c3cb02d9f883ae629 + URL https://github.com/kcat/openal-soft/archive/ef44737658313cc0a6394eed7878ad28d36fdca5.zip + URL_HASH SHA512=66b1e5a373ab6f5414220b7b22c46a0e0eba7d83e004088be75bb5ec808e4d4e2152fc14989ba182475c4d1d020332bbd9b3deb1b26f8506d40112f995e02206 OVERRIDE_FIND_PACKAGE ) thirdparty_module(Opus