From 31d9176b0d0845fd439f088b94f8d1fe1ad60ca7 Mon Sep 17 00:00:00 2001 From: OusmBlueNinja <89956790+OusmBlueNinja@users.noreply.github.com> Date: Wed, 30 Apr 2025 12:58:04 -0500 Subject: [PATCH] Added audio player componenet --- imgui.ini | 60 +++-- remake/build.log | 2 - src/assets/scenes/audio_test.cene | 60 +++++ src/src/Components/AudioPlayerComponent.cpp | 100 +++++++ src/src/Components/AudioPlayerComponent.h | 37 +++ src/src/Engine.cpp | 46 +++- src/src/Entitys/Object.cpp | 4 + src/src/core/audio/AudioEngine.cpp | 111 ++++---- src/src/core/audio/AudioEngine.h | 22 +- src/src/core/data/icons/lightbulb-on-20.png | Bin 0 -> 8396 bytes src/src/core/data/icons/sound.h | 0 src/src/core/data/icons/video.png | Bin 0 -> 3751 bytes src/src/core/data/icons/volume-high.png | Bin 0 -> 7237 bytes src/src/core/utils/AssetManager.cpp | 278 ++++++++++++-------- src/src/editor/windows/Inspector.cpp | 55 +++- 15 files changed, 591 insertions(+), 184 deletions(-) create mode 100644 src/assets/scenes/audio_test.cene create mode 100644 src/src/Components/AudioPlayerComponent.cpp create mode 100644 src/src/Components/AudioPlayerComponent.h create mode 100644 src/src/core/data/icons/lightbulb-on-20.png create mode 100644 src/src/core/data/icons/sound.h create mode 100644 src/src/core/data/icons/video.png create mode 100644 src/src/core/data/icons/volume-high.png diff --git a/imgui.ini b/imgui.ini index bf9d838..dc774bd 100644 --- a/imgui.ini +++ b/imgui.ini @@ -14,10 +14,10 @@ Size=1280,701 Collapsed=0 [Window][Inspector] -Pos=774,19 -Size=506,701 +Pos=831,19 +Size=449,439 Collapsed=0 -DockId=0x00000006,0 +DockId=0x0000000D,0 [Window][Scene Tree] Pos=0,19 @@ -27,7 +27,7 @@ DockId=0x00000003,0 [Window][Viewport] Pos=344,19 -Size=428,390 +Size=485,390 Collapsed=0 DockId=0x00000007,0 @@ -37,13 +37,13 @@ Collapsed=0 [Window][Performance Info] Pos=344,411 -Size=428,309 +Size=485,309 Collapsed=0 DockId=0x00000008,2 [Window][Console] Pos=344,411 -Size=428,309 +Size=485,309 Collapsed=0 DockId=0x00000008,0 @@ -55,7 +55,7 @@ DockId=0x00000007,1 [Window][Profiler] Pos=344,411 -Size=428,309 +Size=485,309 Collapsed=0 DockId=0x00000008,3 @@ -79,7 +79,7 @@ DockId=0x00000008,1 [Window][Color Correction] Pos=344,411 -Size=428,309 +Size=485,309 Collapsed=0 DockId=0x00000008,1 @@ -94,14 +94,38 @@ Pos=797,551 Size=325,75 Collapsed=0 -[Docking][Data] -DockSpace ID=0x11111111 Window=0x1BBC0F80 Pos=0,19 Size=1280,701 Split=X - DockNode ID=0x00000005 Parent=0x11111111 SizeRef=772,1158 Split=X - DockNode ID=0x00000001 Parent=0x00000005 SizeRef=342,701 Split=Y Selected=0x12EF0F59 - DockNode ID=0x00000003 Parent=0x00000001 SizeRef=342,575 HiddenTabBar=1 Selected=0x12EF0F59 - DockNode ID=0x00000004 Parent=0x00000001 SizeRef=342,581 HiddenTabBar=1 Selected=0x36AF052B - DockNode ID=0x00000002 Parent=0x00000005 SizeRef=428,701 Split=Y Selected=0xC450F867 - DockNode ID=0x00000007 Parent=0x00000002 SizeRef=606,847 CentralNode=1 HiddenTabBar=1 Selected=0xC450F867 - DockNode ID=0x00000008 Parent=0x00000002 SizeRef=606,309 Selected=0x9B5D3198 - DockNode ID=0x00000006 Parent=0x11111111 SizeRef=506,1158 HiddenTabBar=1 Selected=0x36DC96AB +[Window][Audio VU] +Pos=831,509 +Size=449,211 +Collapsed=0 +DockId=0x0000000A,0 + +[Window][Audio Mixer] +Pos=831,577 +Size=449,143 +Collapsed=0 +DockId=0x0000000C,0 + +[Window][Playing Audio] +Pos=831,460 +Size=449,260 +Collapsed=0 +DockId=0x0000000E,0 + +[Docking][Data] +DockSpace ID=0x11111111 Window=0x1BBC0F80 Pos=0,19 Size=1280,701 Split=X + DockNode ID=0x00000005 Parent=0x11111111 SizeRef=829,1158 Split=X + DockNode ID=0x00000001 Parent=0x00000005 SizeRef=342,701 Split=Y Selected=0x12EF0F59 + DockNode ID=0x00000003 Parent=0x00000001 SizeRef=342,575 HiddenTabBar=1 Selected=0x12EF0F59 + DockNode ID=0x00000004 Parent=0x00000001 SizeRef=342,581 HiddenTabBar=1 Selected=0x36AF052B + DockNode ID=0x00000002 Parent=0x00000005 SizeRef=485,701 Split=Y Selected=0xC450F867 + DockNode ID=0x00000007 Parent=0x00000002 SizeRef=606,847 CentralNode=1 HiddenTabBar=1 Selected=0xC450F867 + DockNode ID=0x00000008 Parent=0x00000002 SizeRef=606,309 Selected=0x9B5D3198 + DockNode ID=0x00000006 Parent=0x11111111 SizeRef=449,1158 Split=Y Selected=0x36DC96AB + DockNode ID=0x00000009 Parent=0x00000006 SizeRef=449,488 Split=Y Selected=0x36DC96AB + DockNode ID=0x0000000B Parent=0x00000009 SizeRef=449,556 Split=Y Selected=0x36DC96AB + DockNode ID=0x0000000D Parent=0x0000000B SizeRef=449,439 HiddenTabBar=1 Selected=0x36DC96AB + DockNode ID=0x0000000E Parent=0x0000000B SizeRef=449,260 HiddenTabBar=1 Selected=0x9D7E7171 + DockNode ID=0x0000000C Parent=0x00000009 SizeRef=449,143 HiddenTabBar=1 Selected=0xB6C74292 + DockNode ID=0x0000000A Parent=0x00000006 SizeRef=449,211 HiddenTabBar=1 Selected=0xD83E5DD3 diff --git a/remake/build.log b/remake/build.log index f4caab9..e69de29 100644 --- a/remake/build.log +++ b/remake/build.log @@ -1,2 +0,0 @@ -[LINK] g++ src\build\Engine.o src\build\main.o src\build\Renderer.o src\build\Components\AnimationComponent.o src\build\Components\CameraComponent.o src\build\Components\LightComponent.o src\build\Components\ParticleComponent.o src\build\Components\PhysicsComponent.o src\build\Components\ScriptComponent.o src\build\Components\SpriteComponent.o src\build\Components\TextComonent.o src\build\Components\TilemapComponent.o src\build\core\audio\AudioEngine.o src\build\core\utils\AssetManager.o src\build\core\utils\EngineConfig.o src\build\core\utils\ExceptionHandler.o src\build\core\utils\FileDialog.o src\build\core\utils\input.o src\build\core\utils\LoadingWindow.o src\build\core\utils\Logging.o src\build\core\utils\Profiler.o src\build\core\utils\Texture.o src\build\core\utils\utils.o src\build\editor\windows\AssetBrowser.o src\build\editor\windows\Inspector.o src\build\Entitys\Object.o src\build\utils\GameObjectsList.o src\build\utils\Shader.o src\build\utils\UID.o src\build\lapi.o src\build\lauxlib.o src\build\lbaselib.o src\build\lcode.o src\build\lcorolib.o src\build\lctype.o src\build\ldblib.o src\build\ldebug.o src\build\ldo.o src\build\ldump.o src\build\lfunc.o src\build\lgc.o src\build\linit.o src\build\liolib.o src\build\llex.o src\build\lmathlib.o src\build\lmem.o src\build\loadlib.o src\build\lobject.o src\build\lopcodes.o src\build\loslib.o src\build\lparser.o src\build\lstate.o src\build\lstring.o src\build\lstrlib.o src\build\ltable.o src\build\ltablib.o src\build\ltm.o src\build\lua.o src\build\luac.o src\build\lundump.o src\build\lutf8lib.o src\build\lvm.o src\build\lzio.o src\build\imgui.o src\build\imgui_demo.o src\build\imgui_draw.o src\build\imgui_impl_glfw.o src\build\imgui_impl_opengl3.o src\build\imgui_tables.o src\build\imgui_widgets.o src\build\aabb.o src\build\arena_allocator.o src\build\array.o src\build\bitset.o src\build\body.o src\build\broad_phase.o src\build\constraint_graph.o src\build\contact.o src\build\contact_solver.o src\build\core.o src\build\distance.o src\build\distance_joint.o src\build\dynamic_tree.o src\build\geometry.o src\build\hull.o src\build\id_pool.o src\build\island.o src\build\joint.o src\build\manifold.o src\build\math_functions.o src\build\motor_joint.o src\build\mouse_joint.o src\build\mover.o src\build\prismatic_joint.o src\build\revolute_joint.o src\build\sensor.o src\build\shape.o src\build\solver.o src\build\solver_set.o src\build\table.o src\build\timer.o src\build\types.o src\build\weld_joint.o src\build\wheel_joint.o src\build\world.o src\build\xxhash.o -o src\build\app.exe -LC:\msys64\mingw64\lib -lglfw3 -lglew32 -lopengl32 -lgdi32 -lyaml-cpp -lcomdlg32 -lssl -lcrypto -[RUN] Executed app.exe successfully. diff --git a/src/assets/scenes/audio_test.cene b/src/assets/scenes/audio_test.cene new file mode 100644 index 0000000..625e9bf --- /dev/null +++ b/src/assets/scenes/audio_test.cene @@ -0,0 +1,60 @@ +engine_version: 0.1.0 +scene_name: audio_test +scene_hash: 2f3968cbbb1905084ea57585a1e25851d20881c0027323ccc1f8b56bc53c90c0 +format_version: 1 +objects: + - name: Hello, Create + uid: 4afb134ba5a84d3d837374e314ca9adb + id: 0 + position: [0, 0] + rotation: 0 + layer: 0 + visable: true + components: + - type: AudioPlayerComponent + uaid: 1 + volume: 1 + loop: false + children: [] +color_correction: + brightness: 1 + saturation: 1 + gamma: 1 + bloom: true + intensity: 1.20000005 + threshold: 1 +Assets: + - uaid: 4 + path: C:\Users\spenc\OneDrive\Pictures\textures\ganges_river_pebbles_nor_gl_1k.png + filename: ganges_river_pebbles_nor_gl_1k.png + filetype: png + type: 0 + hash: 6711b00700d4c94a + lastModified: 1744565594 + size: [1024, 1024] + channels: 4 + format: GL_RGBA + - uaid: 3 + path: C:\Users\spenc\OneDrive\Pictures\textures\ganges_river_pebbles_diff_1k.png + filename: ganges_river_pebbles_diff_1k.png + filetype: png + type: 0 + hash: 0349580fcbf62155 + lastModified: 1744565606 + size: [1024, 1024] + channels: 4 + format: GL_RGBA + - uaid: 2 + path: C:\Users\spenc\Music\simple-notification-152054.mp3 + filename: simple-notification-152054.mp3 + filetype: mp3 + type: 1 + hash: 3e57c2530f08c1ab + lastModified: 1745951639 + - uaid: 1 + path: C:\Users\spenc\Music\simple-notification-152054.wav + filename: simple-notification-152054.wav + filetype: wav + type: 1 + hash: 0f5adca8b95e7494 + lastModified: 1745953000 \ No newline at end of file diff --git a/src/src/Components/AudioPlayerComponent.cpp b/src/src/Components/AudioPlayerComponent.cpp new file mode 100644 index 0000000..028ff45 --- /dev/null +++ b/src/src/Components/AudioPlayerComponent.cpp @@ -0,0 +1,100 @@ +#include "AudioPlayerComponent.h" +#include "../core/utils/Logging.h" + +AudioPlayerComponent::AudioPlayerComponent(Object* owner) + : Component(owner) {} + +AudioPlayerComponent::~AudioPlayerComponent() +{ + if (m_OwnsSound && m_Sound) + { + ma_sound_uninit(m_Sound); + delete m_Sound; + } +} + +void AudioPlayerComponent::SetAudio(uint64_t uaid) +{ + Stop(); + m_UAID = uaid; + + const auto* asset = dynamic_cast(AssetManager::GetAssetByID(uaid)); + if (!asset || !asset->loaded) + { + Logger::LogWarning("AudioPlayerComponent: Asset not found or not loaded (UAID: %llu)", uaid); + m_Sound = nullptr; + return; + } + + m_Sound = asset->sound; + m_OwnsSound = false; + + ma_sound_set_volume(m_Sound, volume); + ma_sound_set_looping(m_Sound, loop ? MA_TRUE : MA_FALSE); +} + +void AudioPlayerComponent::Play() +{ + if (m_Sound) ma_sound_start(m_Sound); +} + +void AudioPlayerComponent::Pause() +{ + if (m_Sound) ma_sound_stop(m_Sound); +} + +void AudioPlayerComponent::Stop() +{ + if (m_Sound) + { + ma_sound_stop(m_Sound); + ma_sound_seek_to_pcm_frame(m_Sound, 0); + } +} + +void AudioPlayerComponent::SetLooping(bool value) +{ + loop = value; + if (m_Sound) ma_sound_set_looping(m_Sound, value ? MA_TRUE : MA_FALSE); +} + +void AudioPlayerComponent::SetVolume(float vol) +{ + volume = vol; + if (m_Sound) ma_sound_set_volume(m_Sound, vol); +} + +bool AudioPlayerComponent::IsPlaying() const +{ + return m_Sound && ma_sound_is_playing(m_Sound); +} + +std::string AudioPlayerComponent::GetName() const +{ + return "AudioPlayerComponent"; +} + +void AudioPlayerComponent::Save(YAML::Emitter& out) const +{ + + out << YAML::BeginMap; + out << YAML::Key << "type" << YAML::Value << GetName(); + out << YAML::Key << "uaid" << YAML::Value << m_UAID; + out << YAML::Key << "volume" << YAML::Value << volume; + out << YAML::Key << "loop" << YAML::Value << loop; + out << YAML::EndMap; + +} + +void AudioPlayerComponent::Load(const YAML::Node& node) +{ + if (node["uaid"]) + { + m_UAID = node["uaid"].as(); + SetAudio(m_UAID); + } + if (node["volume"]) + SetVolume(node["volume"].as()); + if (node["loop"]) + SetLooping(node["loop"].as()); +} diff --git a/src/src/Components/AudioPlayerComponent.h b/src/src/Components/AudioPlayerComponent.h new file mode 100644 index 0000000..6cfbab3 --- /dev/null +++ b/src/src/Components/AudioPlayerComponent.h @@ -0,0 +1,37 @@ +#pragma once +#include "Component.h" +#include "../core/audio/AudioEngine.h" +#include "../core/utils/AssetManager.h" +#include + +class AudioPlayerComponent : public Component +{ +public: + AudioPlayerComponent(Object* owner); + ~AudioPlayerComponent(); + + void SetAudio(uint64_t uaid); + void Play(); + void Pause(); + void Stop(); + + void SetLooping(bool loop); + void SetVolume(float vol); + + bool IsPlaying() const; + + // Serialization + std::string GetName() const override; + void Save(YAML::Emitter& out) const override; + void Load(const YAML::Node& node) override; + + uint64_t GetUAID() const { return m_UAID; } + +private: + ma_sound* m_Sound = nullptr; + uint64_t m_UAID = 0; + bool m_OwnsSound = false; + + float volume = 1.0f; + bool loop = false; +}; diff --git a/src/src/Engine.cpp b/src/src/Engine.cpp index df754a5..f200afe 100644 --- a/src/src/Engine.cpp +++ b/src/src/Engine.cpp @@ -15,7 +15,6 @@ #include "core/audio/AudioEngine.h" - #include "core/utils/EngineConfig.h" #include "utils/GameObjectsList.h" #include "core/utils/Profiler.h" @@ -517,8 +516,6 @@ void Engine::Init() AudioEngine::Init(); - - Logger::LogVerbose("Resverving Objects"); // These values were AI Generated. @@ -540,6 +537,44 @@ core::types::Vec2 ScreenToWorld(const core::types::Vec2 &screenPos, const core:: return world; } + + +void DrawAudioPlayingList() +{ + ImGui::Begin("Playing Audio"); + + std::vector toStop; + + for (const auto& [uaid, psound] : AudioEngine::GetPlayingSounds()) + { + ImGui::Text("%s", psound.name.c_str()); + + if (psound.totalTime > 0.0) + { + float progress = static_cast(psound.currentTime / psound.totalTime); + ImGui::ProgressBar(progress, ImVec2(-1.0f, 6.0f)); + } + + ImGui::Text("Time: %.2f / %.2f sec", psound.currentTime, psound.totalTime); + + if (ImGui::Button(("Stop##" + std::to_string(uaid)).c_str())) + { + toStop.push_back(uaid); + } + + ImGui::Separator(); + } + + ImGui::End(); + + for (uint64_t id : toStop) + AudioEngine::Stop(id); +} + + + + + void Engine::collectObjects(bool playing, const glm::vec2 &camPos, float camZoom) { @@ -743,7 +778,7 @@ void Engine::Run() ShowColorCorrectionWindow(); ShowAssetBrowser(); - + DrawAudioPlayingList(); { PROFILE_ENGINE_SCOPE("Engine::DrawSceneTree"); @@ -947,6 +982,8 @@ void Engine::Run() profiler.EndSection(); + AudioEngine::Update(); + profiler.BeginSection("Render"); for (auto *obj : m_toDraw) { @@ -1563,6 +1600,5 @@ void Engine::Shutdown() AudioEngine::Shutdown(); - std::filesystem::remove(tempScenePath); } diff --git a/src/src/Entitys/Object.cpp b/src/src/Entitys/Object.cpp index d707ce0..7d2ee11 100644 --- a/src/src/Entitys/Object.cpp +++ b/src/src/Entitys/Object.cpp @@ -8,6 +8,8 @@ #include "../Components/ScriptComponent.h" #include "../Components/ParticleComponent.h" #include "../Components/AnimationComponent.h" +#include "../Components/AudioPlayerComponent.h" + #include "../core/utils/Logging.h" #include "../utils/UID.h" @@ -165,6 +167,8 @@ void Object::Load(const YAML::Node &node) AddComponent()->Load(compNode); else if (type == "AnimationComponent") AddComponent()->Load(compNode); + else if (type == "AudioPlayerComponent") + AddComponent()->Load(compNode); else { Logger::LogError("Invalid Componenet type '%s' found in component '%s'(%d)", type.c_str(), name.c_str(), uid.id); diff --git a/src/src/core/audio/AudioEngine.cpp b/src/src/core/audio/AudioEngine.cpp index 653cc21..84f4051 100644 --- a/src/src/core/audio/AudioEngine.cpp +++ b/src/src/core/audio/AudioEngine.cpp @@ -2,80 +2,95 @@ #define MA_ENABLE_WAV #define MA_ENABLE_FLAC #define MA_ENABLE_VORBIS +#define MA_IMPLEMENTATION #include "AudioEngine.h" #include "../utils/AssetManager.h" #include "../utils/Logging.h" ma_engine AudioEngine::s_Engine{}; -std::unordered_map AudioEngine::s_SoundMap; +std::unordered_map AudioEngine::s_PlayingSounds; -bool AudioEngine::Init() -{ - ma_result result = ma_engine_init(nullptr, &s_Engine); - if (result != MA_SUCCESS) - { - Logger::LogError("Failed to start AudioCore: %s", MiniaudioResultToString(result)); +bool AudioEngine::Init() { + if (ma_engine_init(nullptr, &s_Engine) != MA_SUCCESS) { + Logger::LogError("Failed to start AudioCore"); return false; } - Logger::LogVerbose("AudioCore initialized successfully."); return true; } - -void AudioEngine::Shutdown() -{ - for (auto& [_, sound] : s_SoundMap) - ma_sound_uninit(&sound); - s_SoundMap.clear(); +void AudioEngine::Shutdown() { + for (auto& [_, s] : s_PlayingSounds) + s.Stop(); + s_PlayingSounds.clear(); ma_engine_uninit(&s_Engine); } -void AudioEngine::Play(uint64_t uaid, bool loop) -{ - if (s_SoundMap.find(uaid) == s_SoundMap.end()) - { +void AudioEngine::Play(uint64_t uaid, bool loop) { + if (s_PlayingSounds.find(uaid) == s_PlayingSounds.end()) { const auto* asset = AssetManager::GetAssetByID(uaid); - if (!asset || !asset->loaded || asset->type != AssetType::Audio) - { + if (!asset || !asset->loaded || asset->type != AssetType::Audio) { Logger::LogError("[AudioEngine] Invalid or unloaded audio asset: %llu", uaid); return; } ma_sound sound; - if (ma_sound_init_from_file(&s_Engine, asset->path.c_str(), 0, NULL, NULL, &sound) != MA_SUCCESS) - { - Logger::LogError("[AudioEngine] Failed to initialize sound from: %s", asset->path.c_str()); + if (ma_sound_init_from_file(&s_Engine, asset->path.c_str(), 0, nullptr, nullptr, &sound) != MA_SUCCESS) { + Logger::LogError("[AudioEngine] Failed to load sound: %s", asset->path.c_str()); return; } - s_SoundMap[uaid] = sound; - } + ma_sound_set_looping(&sound, loop); - auto& sound = s_SoundMap[uaid]; - ma_sound_set_looping(&sound, loop); - ma_sound_start(&sound); -} + ma_uint64 totalFrames = 0; + ma_sound_get_length_in_pcm_frames(&sound, &totalFrames); + ma_uint32 sampleRate = ma_engine_get_sample_rate(&s_Engine); + double totalSeconds = sampleRate > 0 ? double(totalFrames) / sampleRate : 0.0; -void AudioEngine::Stop(uint64_t uaid) -{ - auto it = s_SoundMap.find(uaid); - if (it != s_SoundMap.end()) - ma_sound_stop(&it->second); -} + PlayingSound ps{ uaid, asset->filename, sound, 0.0, totalSeconds }; + s_PlayingSounds[uaid] = ps; -void AudioEngine::StopAll() -{ - for (auto& [_, sound] : s_SoundMap) - ma_sound_stop(&sound); -} - -void AudioEngine::Unload(uint64_t uaid) -{ - auto it = s_SoundMap.find(uaid); - if (it != s_SoundMap.end()) - { - ma_sound_uninit(&it->second); - s_SoundMap.erase(it); + ma_sound_start(&s_PlayingSounds[uaid].sound); + } else { + auto& s = s_PlayingSounds[uaid]; + ma_sound_set_looping(&s.sound, loop); + ma_sound_start(&s.sound); } } + +void AudioEngine::Stop(uint64_t uaid) { + auto it = s_PlayingSounds.find(uaid); + if (it != s_PlayingSounds.end()) { + it->second.Stop(); + s_PlayingSounds.erase(it); + } +} + +void AudioEngine::StopAll() { + for (auto& [_, s] : s_PlayingSounds) + s.Stop(); + s_PlayingSounds.clear(); +} + +void AudioEngine::Unload(uint64_t uaid) { + Stop(uaid); +} + +void AudioEngine::Update() { + for (auto it = s_PlayingSounds.begin(); it != s_PlayingSounds.end();) { + if (!ma_sound_is_playing(&it->second.sound)) { + it->second.Stop(); + it = s_PlayingSounds.erase(it); + } else { + ma_uint64 cursorFrames = 0; + ma_sound_get_cursor_in_pcm_frames(&it->second.sound, &cursorFrames); + ma_uint32 sampleRate = ma_engine_get_sample_rate(&s_Engine); + it->second.currentTime = sampleRate > 0 ? static_cast(cursorFrames) / sampleRate : 0.0; + ++it; + } + } +} + +const std::unordered_map& AudioEngine::GetPlayingSounds() { + return s_PlayingSounds; +} diff --git a/src/src/core/audio/AudioEngine.h b/src/src/core/audio/AudioEngine.h index 2ed72c0..24b862c 100644 --- a/src/src/core/audio/AudioEngine.h +++ b/src/src/core/audio/AudioEngine.h @@ -2,10 +2,22 @@ #include #include #include "miniaudio.h" -#include +#include -class AudioEngine -{ +struct PlayingSound { + uint64_t uaid; + std::string name; + ma_sound sound; + double currentTime = 0.0; + double totalTime = 0.0; + + void Stop() { + ma_sound_stop(&sound); + ma_sound_uninit(&sound); + } +}; + +class AudioEngine { public: static bool Init(); static void Shutdown(); @@ -14,10 +26,12 @@ public: static void Stop(uint64_t uaid); static void StopAll(); static void Unload(uint64_t uaid); + static void Update(); static ma_engine* GetEngine() { return &s_Engine; } + static const std::unordered_map& GetPlayingSounds(); private: static ma_engine s_Engine; - static std::unordered_map s_SoundMap; + static std::unordered_map s_PlayingSounds; }; diff --git a/src/src/core/data/icons/lightbulb-on-20.png b/src/src/core/data/icons/lightbulb-on-20.png new file mode 100644 index 0000000000000000000000000000000000000000..03b8ded53da3609f58256fd1324b73399795af2f GIT binary patch literal 8396 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 literal 0 HcmV?d00001 diff --git a/src/src/core/data/icons/sound.h b/src/src/core/data/icons/sound.h new file mode 100644 index 0000000..e69de29 diff --git a/src/src/core/data/icons/video.png b/src/src/core/data/icons/video.png new file mode 100644 index 0000000000000000000000000000000000000000..5bcb6fc8a7a565bedb21ed19edcc7b1e60d7ca5f GIT binary patch literal 3751 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 literal 0 HcmV?d00001 diff --git a/src/src/core/data/icons/volume-high.png b/src/src/core/data/icons/volume-high.png new file mode 100644 index 0000000000000000000000000000000000000000..6e4ac748d6c635fe07f0621812f5ef7ceea718d5 GIT binary patch literal 7237 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 MNZ(AaR2LulKl-bzUjP6A literal 0 HcmV?d00001 diff --git a/src/src/core/utils/AssetManager.cpp b/src/src/core/utils/AssetManager.cpp index 30a69b9..1d508a5 100644 --- a/src/src/core/utils/AssetManager.cpp +++ b/src/src/core/utils/AssetManager.cpp @@ -6,103 +6,169 @@ #define STB_IMAGE_IMPLEMENTATION #include -#define MINIAUDIO_IMPLEMENTATION -#define MA_ENABLE_MP3 -#define MA_ENABLE_WAV -#define MA_ENABLE_FLAC -#define MA_ENABLE_VORBIS -#include "miniaudio.h" + std::unordered_map> AssetManager::s_Assets; std::unordered_map AssetManager::s_PathToUAID; uint64_t AssetManager::s_NextUAID = 1; - -const char* MiniaudioResultToString(ma_result result) +const char *MiniaudioResultToString(ma_result result) { switch (result) { - case MA_SUCCESS: return "Success"; - case MA_ERROR: return "Generic error"; - case MA_INVALID_ARGS: return "Invalid arguments"; - case MA_INVALID_OPERATION: return "Invalid operation"; - case MA_OUT_OF_MEMORY: return "Out of memory"; - case MA_OUT_OF_RANGE: return "Out of range"; - case MA_ACCESS_DENIED: return "Access denied"; - case MA_DOES_NOT_EXIST: return "File does not exist"; - case MA_ALREADY_EXISTS: return "File already exists"; - case MA_TOO_MANY_OPEN_FILES: return "Too many open files"; - case MA_INVALID_FILE: return "Invalid or corrupted file"; - case MA_TOO_BIG: return "File too big"; - case MA_PATH_TOO_LONG: return "Path too long"; - case MA_NAME_TOO_LONG: return "Name too long"; - case MA_NOT_DIRECTORY: return "Not a directory"; - case MA_IS_DIRECTORY: return "Is a directory"; - case MA_DIRECTORY_NOT_EMPTY: return "Directory not empty"; - case MA_AT_END: return "End of file or stream"; - case MA_NO_SPACE: return "No space left on device"; - case MA_BUSY: return "Resource busy"; - case MA_IO_ERROR: return "I/O error"; - case MA_INTERRUPT: return "Operation interrupted"; - case MA_UNAVAILABLE: return "Resource unavailable"; - case MA_ALREADY_IN_USE: return "Already in use"; - case MA_BAD_ADDRESS: return "Bad memory address"; - case MA_BAD_SEEK: return "Bad seek operation"; - case MA_BAD_PIPE: return "Bad pipe"; - case MA_DEADLOCK: return "Deadlock detected"; - case MA_TOO_MANY_LINKS: return "Too many symbolic links"; - case MA_NOT_IMPLEMENTED: return "Not implemented"; - case MA_NO_MESSAGE: return "No message available"; - case MA_BAD_MESSAGE: return "Bad message format"; - case MA_NO_DATA_AVAILABLE: return "No data available"; - case MA_INVALID_DATA: return "Invalid data"; - case MA_TIMEOUT: return "Operation timed out"; - case MA_NO_NETWORK: return "Network unavailable"; - case MA_NOT_UNIQUE: return "Not unique"; - case MA_NOT_SOCKET: return "Not a socket"; - case MA_NO_ADDRESS: return "No address available"; - case MA_BAD_PROTOCOL: return "Bad protocol"; - case MA_PROTOCOL_UNAVAILABLE: return "Protocol unavailable"; - case MA_PROTOCOL_NOT_SUPPORTED: return "Protocol not supported"; - case MA_PROTOCOL_FAMILY_NOT_SUPPORTED: return "Protocol family not supported"; - case MA_ADDRESS_FAMILY_NOT_SUPPORTED: return "Address family not supported"; - case MA_SOCKET_NOT_SUPPORTED: return "Socket not supported"; - case MA_CONNECTION_RESET: return "Connection reset"; - case MA_ALREADY_CONNECTED: return "Already connected"; - case MA_NOT_CONNECTED: return "Not connected"; - case MA_CONNECTION_REFUSED: return "Connection refused"; - case MA_NO_HOST: return "Host not found"; - case MA_IN_PROGRESS: return "Operation in progress"; - case MA_CANCELLED: return "Operation cancelled"; - case MA_MEMORY_ALREADY_MAPPED: return "Memory already mapped"; - - case MA_CRC_MISMATCH: return "CRC mismatch"; - - case MA_FORMAT_NOT_SUPPORTED: return "Audio format not supported"; - case MA_DEVICE_TYPE_NOT_SUPPORTED: return "Device type not supported"; - case MA_SHARE_MODE_NOT_SUPPORTED: return "Share mode not supported"; - case MA_NO_BACKEND: return "No backend available"; - case MA_NO_DEVICE: return "No device available"; - case MA_API_NOT_FOUND: return "API not found"; - case MA_INVALID_DEVICE_CONFIG: return "Invalid device configuration"; - case MA_LOOP: return "Loop detected"; - case MA_BACKEND_NOT_ENABLED: return "Backend not enabled"; - - case MA_DEVICE_NOT_INITIALIZED: return "Device not initialized"; - case MA_DEVICE_ALREADY_INITIALIZED: return "Device already initialized"; - case MA_DEVICE_NOT_STARTED: return "Device not started"; - case MA_DEVICE_NOT_STOPPED: return "Device not stopped"; - - case MA_FAILED_TO_INIT_BACKEND: return "Failed to initialize backend"; - case MA_FAILED_TO_OPEN_BACKEND_DEVICE: return "Failed to open backend device"; - case MA_FAILED_TO_START_BACKEND_DEVICE: return "Failed to start backend device"; - case MA_FAILED_TO_STOP_BACKEND_DEVICE: return "Failed to stop backend device"; - - default: return "Unknown error code"; + case MA_SUCCESS: + return "Success"; + case MA_ERROR: + return "Generic error"; + case MA_INVALID_ARGS: + return "Invalid arguments"; + case MA_INVALID_OPERATION: + return "Invalid operation"; + case MA_OUT_OF_MEMORY: + return "Out of memory"; + case MA_OUT_OF_RANGE: + return "Out of range"; + case MA_ACCESS_DENIED: + return "Access denied"; + case MA_DOES_NOT_EXIST: + return "File does not exist"; + case MA_ALREADY_EXISTS: + return "File already exists"; + case MA_TOO_MANY_OPEN_FILES: + return "Too many open files"; + case MA_INVALID_FILE: + return "Invalid or corrupted file"; + case MA_TOO_BIG: + return "File too big"; + case MA_PATH_TOO_LONG: + return "Path too long"; + case MA_NAME_TOO_LONG: + return "Name too long"; + case MA_NOT_DIRECTORY: + return "Not a directory"; + case MA_IS_DIRECTORY: + return "Is a directory"; + case MA_DIRECTORY_NOT_EMPTY: + return "Directory not empty"; + case MA_AT_END: + return "End of file or stream"; + case MA_NO_SPACE: + return "No space left on device"; + case MA_BUSY: + return "Resource busy"; + case MA_IO_ERROR: + return "I/O error"; + case MA_INTERRUPT: + return "Operation interrupted"; + case MA_UNAVAILABLE: + return "Resource unavailable"; + case MA_ALREADY_IN_USE: + return "Already in use"; + case MA_BAD_ADDRESS: + return "Bad memory address"; + case MA_BAD_SEEK: + return "Bad seek operation"; + case MA_BAD_PIPE: + return "Bad pipe"; + case MA_DEADLOCK: + return "Deadlock detected"; + case MA_TOO_MANY_LINKS: + return "Too many symbolic links"; + case MA_NOT_IMPLEMENTED: + return "Not implemented"; + case MA_NO_MESSAGE: + return "No message available"; + case MA_BAD_MESSAGE: + return "Bad message format"; + case MA_NO_DATA_AVAILABLE: + return "No data available"; + case MA_INVALID_DATA: + return "Invalid data"; + case MA_TIMEOUT: + return "Operation timed out"; + case MA_NO_NETWORK: + return "Network unavailable"; + case MA_NOT_UNIQUE: + return "Not unique"; + case MA_NOT_SOCKET: + return "Not a socket"; + case MA_NO_ADDRESS: + return "No address available"; + case MA_BAD_PROTOCOL: + return "Bad protocol"; + case MA_PROTOCOL_UNAVAILABLE: + return "Protocol unavailable"; + case MA_PROTOCOL_NOT_SUPPORTED: + return "Protocol not supported"; + case MA_PROTOCOL_FAMILY_NOT_SUPPORTED: + return "Protocol family not supported"; + case MA_ADDRESS_FAMILY_NOT_SUPPORTED: + return "Address family not supported"; + case MA_SOCKET_NOT_SUPPORTED: + return "Socket not supported"; + case MA_CONNECTION_RESET: + return "Connection reset"; + case MA_ALREADY_CONNECTED: + return "Already connected"; + case MA_NOT_CONNECTED: + return "Not connected"; + case MA_CONNECTION_REFUSED: + return "Connection refused"; + case MA_NO_HOST: + return "Host not found"; + case MA_IN_PROGRESS: + return "Operation in progress"; + case MA_CANCELLED: + return "Operation cancelled"; + case MA_MEMORY_ALREADY_MAPPED: + return "Memory already mapped"; + + case MA_CRC_MISMATCH: + return "CRC mismatch"; + + case MA_FORMAT_NOT_SUPPORTED: + return "Audio format not supported"; + case MA_DEVICE_TYPE_NOT_SUPPORTED: + return "Device type not supported"; + case MA_SHARE_MODE_NOT_SUPPORTED: + return "Share mode not supported"; + case MA_NO_BACKEND: + return "No backend available"; + case MA_NO_DEVICE: + return "No device available"; + case MA_API_NOT_FOUND: + return "API not found"; + case MA_INVALID_DEVICE_CONFIG: + return "Invalid device configuration"; + case MA_LOOP: + return "Loop detected"; + case MA_BACKEND_NOT_ENABLED: + return "Backend not enabled"; + + case MA_DEVICE_NOT_INITIALIZED: + return "Device not initialized"; + case MA_DEVICE_ALREADY_INITIALIZED: + return "Device already initialized"; + case MA_DEVICE_NOT_STARTED: + return "Device not started"; + case MA_DEVICE_NOT_STOPPED: + return "Device not stopped"; + case MA_FAILED_TO_INIT_BACKEND: + return "Failed to initialize backend"; + case MA_FAILED_TO_OPEN_BACKEND_DEVICE: + return "Failed to open backend device"; + case MA_FAILED_TO_START_BACKEND_DEVICE: + return "Failed to start backend device"; + case MA_FAILED_TO_STOP_BACKEND_DEVICE: + return "Failed to stop backend device"; + + default: + return "Unknown error code"; } } + void AssetManager::Init() { } @@ -112,7 +178,7 @@ uint64_t AssetManager::GenerateUAID() return s_NextUAID++; } -void AssetManager::LoadAssetAsync(const std::string& path, AssetType type) +void AssetManager::LoadAssetAsync(const std::string &path, AssetType type) { if (auto itPath = s_PathToUAID.find(path); itPath != s_PathToUAID.end() && s_Assets[itPath->second]->loaded) { @@ -147,7 +213,7 @@ void AssetManager::LoadAssetAsync(const std::string& path, AssetType type) LoadAudioInternal(path, uaid); } -void AssetManager::LoadImageInternal(const std::string& path, uint64_t uaid) +void AssetManager::LoadImageInternal(const std::string &path, uint64_t uaid) { auto it = s_Assets.find(uaid); if (it == s_Assets.end()) @@ -168,7 +234,7 @@ void AssetManager::LoadImageInternal(const std::string& path, uint64_t uaid) std::vector fileData((std::istreambuf_iterator(file)), {}); int w, h, channels; - stbi_uc* pixels = stbi_load_from_memory(fileData.data(), static_cast(fileData.size()), &w, &h, &channels, STBI_rgb_alpha); + stbi_uc *pixels = stbi_load_from_memory(fileData.data(), static_cast(fileData.size()), &w, &h, &channels, STBI_rgb_alpha); if (!pixels) { @@ -204,7 +270,7 @@ void AssetManager::LoadImageInternal(const std::string& path, uint64_t uaid) Logger::LogVerbose("[AssetManager] Loaded image: %s (%dx%d)", path.c_str(), w, h); } -void AssetManager::LoadAudioInternal(const std::string& path, uint64_t uaid) +void AssetManager::LoadAudioInternal(const std::string &path, uint64_t uaid) { auto it = s_Assets.find(uaid); if (it == s_Assets.end()) @@ -233,22 +299,19 @@ void AssetManager::LoadAudioInternal(const std::string& path, uint64_t uaid) Logger::LogVerbose("[AssetManager] Loaded audio: %s", path.c_str()); } - - - -const AssetInfo* AssetManager::GetAssetByID(uint64_t uaid) +const AssetInfo *AssetManager::GetAssetByID(uint64_t uaid) { auto it = s_Assets.find(uaid); return it != s_Assets.end() ? it->second.get() : nullptr; } -const AssetInfo* AssetManager::GetAssetByPath(const std::string& path) +const AssetInfo *AssetManager::GetAssetByPath(const std::string &path) { auto it = s_PathToUAID.find(path); return it != s_PathToUAID.end() ? s_Assets[it->second].get() : nullptr; } -const std::unordered_map>& AssetManager::GetAllAssets() +const std::unordered_map> &AssetManager::GetAllAssets() { return s_Assets; } @@ -272,17 +335,17 @@ void AssetManager::UnloadAsset(uint64_t uaid) void AssetManager::ClearAllAssets() { - for (auto& [id, asset] : s_Assets) + for (auto &[id, asset] : s_Assets) UnloadAsset(id); s_Assets.clear(); s_PathToUAID.clear(); } -void AssetManager::Save(YAML::Emitter& out) +void AssetManager::Save(YAML::Emitter &out) { out << YAML::Key << "Assets" << YAML::BeginSeq; - for (const auto& [uaid, asset] : s_Assets) + for (const auto &[uaid, asset] : s_Assets) { out << YAML::BeginMap; out << YAML::Key << "uaid" << YAML::Value << asset->uaid; @@ -298,12 +361,12 @@ void AssetManager::Save(YAML::Emitter& out) out << YAML::EndSeq; } -void AssetManager::Load(const YAML::Node& node) +void AssetManager::Load(const YAML::Node &node) { if (!node) return; - for (const auto& item : node) + for (const auto &item : node) { uint64_t uaid = item["uaid"].as(); std::string path = item["path"].as(); @@ -332,18 +395,22 @@ void AssetManager::Load(const YAML::Node& node) if (uaid >= s_NextUAID) s_NextUAID = uaid + 1; + + if (type == AssetType::Image) + LoadImageInternal(path, uaid); + else if (type == AssetType::Audio) + LoadAudioInternal(path, uaid); } } - -void ImageAssetInfo::Save(YAML::Emitter& out) const +void ImageAssetInfo::Save(YAML::Emitter &out) const { out << YAML::Key << "size" << YAML::Value << YAML::Flow << YAML::BeginSeq << size.x << size.y << YAML::EndSeq; out << YAML::Key << "channels" << YAML::Value << channels; out << YAML::Key << "format" << YAML::Value << format; } -void ImageAssetInfo::Load(const YAML::Node& node) +void ImageAssetInfo::Load(const YAML::Node &node) { if (node["size"]) { @@ -354,6 +421,5 @@ void ImageAssetInfo::Load(const YAML::Node& node) format = node["format"] ? node["format"].as() : "GL_RGBA"; } -void AudioAssetInfo::Save(YAML::Emitter& out) const {} -void AudioAssetInfo::Load(const YAML::Node& node) {} - +void AudioAssetInfo::Save(YAML::Emitter &out) const {} +void AudioAssetInfo::Load(const YAML::Node &node) {} diff --git a/src/src/editor/windows/Inspector.cpp b/src/src/editor/windows/Inspector.cpp index 9c21a25..74abbb2 100644 --- a/src/src/editor/windows/Inspector.cpp +++ b/src/src/editor/windows/Inspector.cpp @@ -9,14 +9,18 @@ #include "../../components/PhysicsComponent.h" #include "../../components/ParticleComponent.h" #include "../../components/AnimationComponent.h" +#include "../../components/AudioPlayerComponent.h" #include "imgui.h" #include "imgui_internal.h" #include + + void DrawInspectorUI(std::shared_ptr selected) { + PROFILE_ENGINE_SCOPE("Engine::DrawInspectorUI"); ImGui::Begin("Inspector", nullptr, ImGuiWindowFlags_NoScrollbar); @@ -79,7 +83,8 @@ void DrawInspectorUI(std::shared_ptr selected) "Script", "Tilemap", "Particle", - "Animation"}; + "Animation", + "Audio Player"}; static int selectedIndex = -1; @@ -115,6 +120,9 @@ void DrawInspectorUI(std::shared_ptr selected) selected->AddComponent(); else if (type == "Animation" && !selected->GetComponent()) selected->AddComponent(); + else if (type == "Audio Player" && !selected->GetComponent()) + selected->AddComponent(); + } if (auto sprite = selected->GetComponent()) @@ -696,5 +704,50 @@ void DrawInspectorUI(std::shared_ptr selected) ImGui::Spacing(); } + if (auto audio = selected->GetComponent()) + { + ImGui::SeparatorText("Audio Player"); + + uint64_t currentUaid = audio->GetUAID(); + const AssetInfo *asset = AssetManager::GetAssetByID(currentUaid); + + std::string filename = asset ? asset->filename : "(None)"; + ImGui::Text("%s", filename.c_str()); + + // Drag & Drop from Asset Browser + if (ImGui::BeginDragDropTarget()) + { + if (const ImGuiPayload *payload = ImGui::AcceptDragDropPayload("ASSET_AUDIO")) + { + if (payload->DataSize == sizeof(uint64_t)) + { + uint64_t newUaid = *(const uint64_t *)payload->Data; + audio->SetAudio(newUaid); + } + } + ImGui::EndDragDropTarget(); + } + + // Play/Pause/Stop buttons + if (ImGui::Button("Play")) + audio->Play(); + ImGui::SameLine(); + if (ImGui::Button("Pause")) + audio->Pause(); + ImGui::SameLine(); + if (ImGui::Button("Stop")) + audio->Stop(); + + // Looping + static bool loop = false; + loop = ImGui::Checkbox("Loop", &loop); + audio->SetLooping(loop); + + // Volume + static float volume = 1.0f; + if (ImGui::SliderFloat("Volume", &volume, 0.0f, 1.0f)) + audio->SetVolume(volume); + } + ImGui::End(); } \ No newline at end of file