diff --git a/imgui.ini b/imgui.ini index dc774bd..7d73abf 100644 --- a/imgui.ini +++ b/imgui.ini @@ -14,10 +14,10 @@ Size=1280,701 Collapsed=0 [Window][Inspector] -Pos=831,19 -Size=449,439 +Pos=991,19 +Size=289,701 Collapsed=0 -DockId=0x0000000D,0 +DockId=0x00000017,0 [Window][Scene Tree] Pos=0,19 @@ -27,7 +27,7 @@ DockId=0x00000003,0 [Window][Viewport] Pos=344,19 -Size=485,390 +Size=645,390 Collapsed=0 DockId=0x00000007,0 @@ -36,16 +36,16 @@ Size=1280,19 Collapsed=0 [Window][Performance Info] -Pos=344,411 -Size=485,309 +Pos=978,592 +Size=302,128 Collapsed=0 -DockId=0x00000008,2 +DockId=0x00000011,0 [Window][Console] Pos=344,411 -Size=485,309 +Size=541,139 Collapsed=0 -DockId=0x00000008,0 +DockId=0x00000013,0 [Window][Tilemap Editor] Pos=265,19 @@ -54,34 +54,34 @@ Collapsed=0 DockId=0x00000007,1 [Window][Profiler] -Pos=344,411 -Size=485,309 +Pos=344,552 +Size=541,168 Collapsed=0 -DockId=0x00000008,3 +DockId=0x00000014,0 [Window][Profiler Timeline] Pos=265,69 Size=623,651 Collapsed=0 -DockId=0x00000008,1 +DockId=0x00000013,1 [Window][Profiler (Unity Style)] Pos=265,430 Size=623,290 Collapsed=0 -DockId=0x00000008,1 +DockId=0x00000013,1 [Window][Profiler Timeline View] Pos=265,526 Size=1263,651 Collapsed=0 -DockId=0x00000008,1 +DockId=0x00000013,1 [Window][Color Correction] -Pos=344,411 -Size=485,309 +Pos=1131,592 +Size=149,128 Collapsed=0 -DockId=0x00000008,1 +DockId=0x00000012,0 [Window][Asset Browser] Pos=0,369 @@ -107,25 +107,47 @@ Collapsed=0 DockId=0x0000000C,0 [Window][Playing Audio] -Pos=831,460 -Size=449,260 +Pos=978,460 +Size=302,260 Collapsed=0 -DockId=0x0000000E,0 +DockId=0x0000000F,0 + +[Window][Audio Output] +Pos=887,411 +Size=102,309 +Collapsed=0 +DockId=0x00000016,0 + +[Window][Master Bus] +Pos=1003,570 +Size=277,150 +Collapsed=0 +DockId=0x00000018,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 +DockSpace ID=0x11111111 Window=0x1BBC0F80 Pos=0,19 Size=1280,701 Split=X + DockNode ID=0x00000005 Parent=0x11111111 SizeRef=989,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=645,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 Split=X Selected=0xEA83D666 + DockNode ID=0x00000015 Parent=0x00000008 SizeRef=541,172 Split=Y Selected=0xEA83D666 + DockNode ID=0x00000013 Parent=0x00000015 SizeRef=553,139 HiddenTabBar=1 Selected=0xEA83D666 + DockNode ID=0x00000014 Parent=0x00000015 SizeRef=553,168 HiddenTabBar=1 Selected=0x9B5D3198 + DockNode ID=0x00000016 Parent=0x00000008 SizeRef=102,172 HiddenTabBar=1 Selected=0x56009A08 + DockNode ID=0x00000006 Parent=0x11111111 SizeRef=289,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 Split=Y Selected=0x36DC96AB + DockNode ID=0x00000017 Parent=0x0000000D SizeRef=277,549 HiddenTabBar=1 Selected=0x36DC96AB + DockNode ID=0x00000018 Parent=0x0000000D SizeRef=277,150 Selected=0x96A8354B + DockNode ID=0x0000000E Parent=0x0000000B SizeRef=449,260 Split=Y Selected=0x9D7E7171 + DockNode ID=0x0000000F Parent=0x0000000E SizeRef=302,215 HiddenTabBar=1 Selected=0x9D7E7171 + DockNode ID=0x00000010 Parent=0x0000000E SizeRef=302,213 Split=X Selected=0x3FC1A724 + DockNode ID=0x00000011 Parent=0x00000010 SizeRef=151,213 HiddenTabBar=1 Selected=0x3FC1A724 + DockNode ID=0x00000012 Parent=0x00000010 SizeRef=149,213 HiddenTabBar=1 Selected=0xA873C17F + 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.yaml b/remake.yaml index 6ecd914..e3ddb59 100644 --- a/remake.yaml +++ b/remake.yaml @@ -6,6 +6,8 @@ src_dirs: - src/vendor/imgui - src/vendor/box2d - src/vendor/xxhash + - src/vendor/miniaudio + # Include directories (-I) diff --git a/remake/build.log b/remake/build.log index e69de29..d8bc0bc 100644 --- a/remake/build.log +++ b/remake/build.log @@ -0,0 +1,3 @@ +[COMPILE] g++ -std=c++20 -Wall -g -Isrc/include -Isrc/include/lua -Isrc/vendor -Isrc/vendor/imgui -Isrc/vendor/box2d -Isrc/vendor/xxhash -Isrc/vendor/miniaudio -IC:/msys64/mingw64/include -Isrc\vendor\imgui -IC:\msys64\mingw64\lib\libyaml-cpp.a -MMD -MP -c src\src\editor\windows\AudioInfo.cpp -o src\build\editor\windows\AudioInfo.o +[LINK] g++ src\build\Engine.o src\build\main.o src\build\Renderer.o src\build\Components\AnimationComponent.o src\build\Components\AudioPlayerComponent.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\AudioInfo.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 src\build\miniaudio.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_testing.cene b/src/assets/scenes/audio_testing.cene new file mode 100644 index 0000000..f94ca9a --- /dev/null +++ b/src/assets/scenes/audio_testing.cene @@ -0,0 +1,53 @@ +engine_version: 0.1.0 +scene_name: audio_testing +scene_hash: 2789578d9c2a7814ff6e4c0ef38fcfb5ae09fb6b1418f89557e43c6649010ed4 +format_version: 1 +objects: + - name: Hello, Create + uid: f4ee58087d1c475784f951128c27d7c2 + id: 0 + position: [0, 0] + rotation: 0 + layer: 0 + visable: true + components: + - type: AudioPlayerComponent + uaid: 2 + volume: 0.158999994 + loop: false + children: [] + - name: NewObject + uid: 2125b52bb74344d680ad4332671c78c2 + id: 1 + position: [0, 0] + rotation: 0 + layer: 0 + visable: true + components: + - type: AudioPlayerComponent + uaid: 1 + volume: 0.0419999994 + loop: false + children: [] +color_correction: + brightness: 1 + saturation: 1 + gamma: 1 + bloom: true + intensity: 1.20000005 + threshold: 1 +Assets: + - uaid: 2 + path: C:\Users\spenc\Music\simple-notification-152054.wav + filename: simple-notification-152054.wav + filetype: wav + type: 1 + hash: 0f5adca8b95e7494 + lastModified: 1745952999 + - uaid: 1 + path: C:\Users\spenc\Music\creative-technology-showreel-241274.mp3 + filename: creative-technology-showreel-241274.mp3 + filetype: mp3 + type: 1 + hash: d7f8e8b2954d438f + lastModified: 1730076792 \ No newline at end of file diff --git a/src/src/Components/AudioPlayerComponent.cpp b/src/src/Components/AudioPlayerComponent.cpp index 028ff45..06e828a 100644 --- a/src/src/Components/AudioPlayerComponent.cpp +++ b/src/src/Components/AudioPlayerComponent.cpp @@ -1,100 +1,77 @@ #include "AudioPlayerComponent.h" #include "../core/utils/Logging.h" +#include "../core/utils/AssetManager.h" +#include "../core/audio/AudioEngine.h" AudioPlayerComponent::AudioPlayerComponent(Object* owner) - : Component(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(); +void AudioPlayerComponent::SetAudio(uint64_t uaid) { 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::Play() { + if (m_UAID == 0) return; + AudioEngine::Play(m_UAID, loop); + AudioEngine::SetVolume(m_UAID, volume); // you’ll need to add this helper } -void AudioPlayerComponent::Pause() -{ - if (m_Sound) ma_sound_stop(m_Sound); +void AudioPlayerComponent::Pause() { + if (m_UAID == 0) return; + AudioEngine::Stop(m_UAID); } -void AudioPlayerComponent::Stop() -{ - if (m_Sound) - { - ma_sound_stop(m_Sound); - ma_sound_seek_to_pcm_frame(m_Sound, 0); +void AudioPlayerComponent::Stop() { + if (m_UAID == 0) return; + AudioEngine::Stop(m_UAID); +} + +void AudioPlayerComponent::Update() { + // drive the engine’s per-frame update + AudioEngine::Update(); + + // now you can, if you like, pull back the current time or VU data + // for your own logic/UI. E.g.: + auto& map = AudioEngine::GetPlayingInfoMap(); + auto it = map.find(m_UAID); + if (it != map.end()) { + const auto& info = it->second; + // info.currentTime, info.totalTime, info.vuLeft, info.vuRight ... } } -void AudioPlayerComponent::SetLooping(bool value) -{ + + +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 (m_UAID != 0) { + AudioEngine::SetLooping(m_UAID, value); } - if (node["volume"]) - SetVolume(node["volume"].as()); - if (node["loop"]) - SetLooping(node["loop"].as()); +} + +void AudioPlayerComponent::SetVolume(float vol) { + volume = vol; + if (m_UAID != 0) { + AudioEngine::SetVolume(m_UAID, vol); + } +} + + + + +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(); + if (node["volume"]) volume = node["volume"].as(); + if (node["loop"]) loop = node["loop"].as(); + // don’t auto-play on load } diff --git a/src/src/Components/AudioPlayerComponent.h b/src/src/Components/AudioPlayerComponent.h index 6cfbab3..e4813e5 100644 --- a/src/src/Components/AudioPlayerComponent.h +++ b/src/src/Components/AudioPlayerComponent.h @@ -1,37 +1,36 @@ #pragma once -#include "Component.h" -#include "../core/audio/AudioEngine.h" -#include "../core/utils/AssetManager.h" -#include -class AudioPlayerComponent : public Component -{ +#include "Component.h" +#include +#include + +class AudioPlayerComponent : public Component { public: AudioPlayerComponent(Object* owner); - ~AudioPlayerComponent(); + ~AudioPlayerComponent() override {} void SetAudio(uint64_t uaid); void Play(); void Pause(); void Stop(); - void SetLooping(bool loop); + void SetLooping(bool value); 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; + void Update(); - float volume = 1.0f; - bool loop = false; + // No longer used: + bool IsPlaying() const { return false; } + std::string GetName() const override { return "AudioPlayerComponent"; } + + void Save(YAML::Emitter& out) const override; + void Load(const YAML::Node& node) override; + +private: + uint64_t m_UAID = 0; + float volume = 1.0f; + bool loop = false; }; diff --git a/src/src/Engine.cpp b/src/src/Engine.cpp index f200afe..3fb2008 100644 --- a/src/src/Engine.cpp +++ b/src/src/Engine.cpp @@ -23,6 +23,7 @@ #include "editor/windows/AssetBrowser.h" #include "editor/windows/Inspector.h" +#include "editor/windows/AudioInfo.h" #include #include @@ -347,6 +348,11 @@ void ShowProfilerTimeline() void Engine::ShowDebugOverlay(float deltaTime) { + if (!g_engineConfig.settings.show_performance_window) + { + return; + } + // === Performance Window === static std::vector fpsHistory; static const int maxFpsHistory = 100; @@ -516,6 +522,8 @@ void Engine::Init() AudioEngine::Init(); + AssetManager::LoadAssetAsync("C:\\Users\\spenc\\Music\\creative-technology-showreel-241274.mp3", AssetType::Audio); + Logger::LogVerbose("Resverving Objects"); // These values were AI Generated. @@ -539,42 +547,6 @@ core::types::Vec2 ScreenToWorld(const core::types::Vec2 &screenPos, const core:: -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) { @@ -752,6 +724,9 @@ void Engine::Run() ImGui::Checkbox("Color Correction", &g_engineConfig.settings.show_color_correction_window); ImGui::Checkbox("Profiler", &g_engineConfig.settings.show_profiler_window); ImGui::Checkbox("Assets", &g_engineConfig.settings.show_asset_window); + ImGui::Checkbox("Performance Info", &g_engineConfig.settings.show_performance_window); + ImGui::Checkbox("Audio Viewer", &g_engineConfig.settings.show_audio_window); + ImGui::EndMenu(); } @@ -779,6 +754,8 @@ void Engine::Run() ShowAssetBrowser(); DrawAudioPlayingList(); + + { PROFILE_ENGINE_SCOPE("Engine::DrawSceneTree"); diff --git a/src/src/core/audio/AudioEngine.cpp b/src/src/core/audio/AudioEngine.cpp index 84f4051..c554cde 100644 --- a/src/src/core/audio/AudioEngine.cpp +++ b/src/src/core/audio/AudioEngine.cpp @@ -1,96 +1,207 @@ -#define MA_ENABLE_MP3 -#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" +#include + +// Static member definitions +ma_engine AudioEngine::s_Engine; +std::unordered_map AudioEngine::s_SoundMap; +std::unordered_map AudioEngine::s_PlayingInfoMap; + + +std::vector AudioEngine::s_MasterVU; -ma_engine AudioEngine::s_Engine{}; -std::unordered_map AudioEngine::s_PlayingSounds; bool AudioEngine::Init() { - if (ma_engine_init(nullptr, &s_Engine) != MA_SUCCESS) { - Logger::LogError("Failed to start AudioCore"); + ma_engine_config config = ma_engine_config_init(); + config.onProcess = AudioEngine::EngineProcessCallback; + config.pProcessUserData = nullptr; + + ma_result r = ma_engine_init(&config, &s_Engine); + if (r != MA_SUCCESS) { + Logger::LogError("AudioEngine: ma_engine_init failed (%d)", r); return false; } - Logger::LogVerbose("AudioCore initialized successfully."); + + Logger::LogVerbose("AudioEngine initialized with master-output callback"); return true; } + void AudioEngine::Shutdown() { - for (auto& [_, s] : s_PlayingSounds) - s.Stop(); - s_PlayingSounds.clear(); + // Uninit & delete every sound + for (auto& kv : s_SoundMap) { + ma_sound_uninit(kv.second); + delete kv.second; + } + s_SoundMap.clear(); + s_PlayingInfoMap.clear(); ma_engine_uninit(&s_Engine); } 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) { - Logger::LogError("[AudioEngine] Invalid or unloaded audio asset: %llu", uaid); - return; - } + // 1) Tear down existing instance (if any) + Cleanup(uaid); - ma_sound sound; - 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; - } - - ma_sound_set_looping(&sound, loop); - - 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; - - PlayingSound ps{ uaid, asset->filename, sound, 0.0, totalSeconds }; - s_PlayingSounds[uaid] = ps; - - 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); + // 2) Fetch and validate asset + const AssetInfo* asset = AssetManager::GetAssetByID(uaid); + if (!asset || !asset->loaded || asset->type != AssetType::Audio) { + Logger::LogError("AudioEngine::Play invalid asset %llu", uaid); + return; } + + // 3) Heap-allocate and init the sound from file + ma_sound* sound = new ma_sound; + if (ma_sound_init_from_file( + &s_Engine, + asset->path.c_str(), + /*flags=*/0, + /*pGroup=*/nullptr, + /*pFence=*/nullptr, + sound + ) != MA_SUCCESS) + { + Logger::LogError("AudioEngine::Play failed to load '%s'", asset->path.c_str()); + delete sound; + return; + } + + // 4) Store and start playback + s_SoundMap[uaid] = sound; + ma_sound_set_looping(sound, loop ? MA_TRUE : MA_FALSE); + ma_sound_start(sound); + + // 5) Compute totalTime + ma_uint64 frames = 0; + ma_sound_get_length_in_pcm_frames(sound, &frames); + double sampleRate = ma_engine_get_sample_rate(&s_Engine); + + // 6) Push PlayingInfo + PlayingInfo info; + info.uaid = uaid; + info.name = asset->filename; + info.currentTime= 0.0; + info.totalTime = double(frames) / sampleRate; + info.looping = loop; + info.hasStarted = false; + info.sound = sound; + + s_PlayingInfoMap[uaid] = info; } void AudioEngine::Stop(uint64_t uaid) { - auto it = s_PlayingSounds.find(uaid); - if (it != s_PlayingSounds.end()) { - it->second.Stop(); - s_PlayingSounds.erase(it); + if (auto it = s_SoundMap.find(uaid); it != s_SoundMap.end()) { + ma_sound_stop(it->second); } } void AudioEngine::StopAll() { - for (auto& [_, s] : s_PlayingSounds) - s.Stop(); - s_PlayingSounds.clear(); + for (auto& kv : s_SoundMap) { + ma_sound_stop(kv.second); + } } void AudioEngine::Unload(uint64_t uaid) { - Stop(uaid); + Cleanup(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); + + + +void AudioEngine::CleanupSound(uint64_t uaid) +{ + auto itSound = s_SoundMap.find(uaid); + if (itSound != s_SoundMap.end()) { + ma_sound_uninit(itSound->second); + delete itSound->second; + s_SoundMap.erase(itSound); + } + +} + + +void AudioEngine::Update() +{ + for (auto it = s_PlayingInfoMap.begin(); it != s_PlayingInfoMap.end(); ) + { + uint64_t uaid = it->first; + PlayingInfo &info = it->second; + ma_sound* s = info.sound; + + bool shouldErase = (!s) || + (!info.looping && info.hasStarted && !ma_sound_is_playing(s)); + + if (shouldErase) { + CleanupSound(uaid); + + it = s_PlayingInfoMap.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; + + if (!info.hasStarted && ma_sound_is_playing(s)) { + info.hasStarted = true; + } + + ma_uint64 cursor = 0; + if (ma_sound_get_cursor_in_pcm_frames(s, &cursor) == MA_SUCCESS) { + info.currentTime = double(cursor) / ma_engine_get_sample_rate(&s_Engine); + } + ++it; } } } -const std::unordered_map& AudioEngine::GetPlayingSounds() { - return s_PlayingSounds; + +void AudioEngine::SetVolume(uint64_t uaid, float v) { + if (auto it = s_SoundMap.find(uaid); it != s_SoundMap.end()) { + ma_sound_set_volume(it->second, v); + } +} + +void AudioEngine::SetLooping(uint64_t uaid, bool loop) { + if (auto it = s_SoundMap.find(uaid); it != s_SoundMap.end()) { + ma_sound_set_looping(it->second, loop ? MA_TRUE : MA_FALSE); + } +} + +void AudioEngine::Cleanup(uint64_t uaid) { + if (auto it = s_SoundMap.find(uaid); it != s_SoundMap.end()) { + ma_sound_uninit(it->second); + delete it->second; + s_SoundMap.erase(it); + } + s_PlayingInfoMap.erase(uaid); +} + +const std::unordered_map& AudioEngine::GetPlayingInfoMap() { + return s_PlayingInfoMap; +} + + +void AudioEngine::EngineProcessCallback( + void* /*pUserData*/, + float* pFramesOut, + ma_uint64 frameCount) +{ + // Fetch the playback device and its channel count + ma_device* pDevice = ma_engine_get_device(&s_Engine); + uint32_t channels = pDevice->playback.channels; + + // Ensure our master VU vector matches the channel count + s_MasterVU.assign(channels, 0.0f); + + // Compute peak per channel + for (ma_uint64 i = 0; i < frameCount; ++i) { + for (uint32_t ch = 0; ch < channels; ++ch) { + float v = fabsf(pFramesOut[i * channels + ch]); + if (v > s_MasterVU[ch]) { + s_MasterVU[ch] = v; + } + } + } + +} + + +const std::vector& AudioEngine::GetMasterVU() +{ + return s_MasterVU; } diff --git a/src/src/core/audio/AudioEngine.h b/src/src/core/audio/AudioEngine.h index 24b862c..d829908 100644 --- a/src/src/core/audio/AudioEngine.h +++ b/src/src/core/audio/AudioEngine.h @@ -1,37 +1,59 @@ +// AudioEngine.h + #pragma once -#include -#include #include "miniaudio.h" #include +#include +#include +#include +#include "../utils/AssetManager.h" +#include "../utils/Logging.h" -struct PlayingSound { +struct PlayingInfo +{ 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); - } + bool looping = false; + bool hasStarted = false; + ma_sound *sound = nullptr; + float vuLeft = 0.0f; + float vuRight = 0.0f; }; -class AudioEngine { +class AudioEngine +{ public: static bool Init(); static void Shutdown(); - static void Play(uint64_t uaid, bool loop = false); static void Stop(uint64_t uaid); static void StopAll(); static void Unload(uint64_t uaid); static void Update(); + static void SetVolume(uint64_t uaid, float volume); + static void SetLooping(uint64_t uaid, bool loop); - static ma_engine* GetEngine() { return &s_Engine; } - static const std::unordered_map& GetPlayingSounds(); + // Now returns a per-channel master peak vector + static const std::vector &GetMasterVU(); + + static const std::unordered_map &GetPlayingInfoMap(); private: static ma_engine s_Engine; - static std::unordered_map s_PlayingSounds; + static std::unordered_map s_SoundMap; + static std::unordered_map s_PlayingInfoMap; + + // Per-channel master peaks + static std::vector s_MasterVU; + + static void CleanupSound(uint64_t uaid); + + static void EngineProcessCallback( + void *pUserData, + float *pFramesOut, + ma_uint64 frameCount); + + static void Cleanup(uint64_t uaid); }; diff --git a/src/src/core/utils/EngineConfig.h b/src/src/core/utils/EngineConfig.h index 4884461..ddadd0d 100644 --- a/src/src/core/utils/EngineConfig.h +++ b/src/src/core/utils/EngineConfig.h @@ -10,7 +10,10 @@ USER_SETTING(lighting_enabled, bool, false) \ USER_SETTING(profile_deep, bool, false) \ USER_SETTING(show_asset_window, bool, false) \ - USER_SETTING(profile_gpu, bool, false) + USER_SETTING(profile_gpu, bool, false) \ + USER_SETTING(show_performance_window, bool, false) \ + USER_SETTING(show_audio_window, bool, false) + struct UserSettings { diff --git a/src/src/editor/windows/AudioInfo.cpp b/src/src/editor/windows/AudioInfo.cpp new file mode 100644 index 0000000..b549cf4 --- /dev/null +++ b/src/src/editor/windows/AudioInfo.cpp @@ -0,0 +1,113 @@ +// AudioInfo.cpp +#include "AudioInfo.h" +#include "../../core/utils/EngineConfig.h" +#include +#include "../../core/audio/AudioEngine.h" +#include "../../core/utils/Profiler.h" +#include +#include + + +void DrawAudioPlayingList() +{ + PROFILE_ENGINE_SCOPE("Engine::DrawAudioPlayingList"); + if (!g_engineConfig.settings.show_audio_window) + return; + + ImGui::SetNextWindowSize(ImVec2(240, 180), ImGuiCond_FirstUseEver); + ImGui::Begin("Audio Output"); + + const auto& masterVU = AudioEngine::GetMasterVU(); + size_t nCh = masterVU.size(); + if (nCh == 0) + { + ImGui::TextDisabled("No audio channels"); + ImGui::End(); + return; + } + + // per-channel smoothing & peak-hold + static std::vector smooth, peakHold, decay; + if (smooth.size() != nCh) { + smooth.assign(nCh, 0.0f); + peakHold.assign(nCh, 0.0f); + decay.assign(nCh, 0.005f); + } + + // layout: compute barW clamped to maxBarW, but no centering + float availW = ImGui::GetContentRegionAvail().x; + const float space = 2.0f; + const float maxBar = 40.0f; // maximum bar width + float idealBarW = (availW - (nCh - 1)*space) / float(nCh); + float barW = std::min(idealBarW, maxBar); + float barH = (ImGui::GetContentRegionAvail().y * 0.90) - 25.0f; + + ImVec2 origin = ImGui::GetCursorScreenPos(); + ImDrawList* dl = ImGui::GetWindowDrawList(); + + // reserve the area for all bars + labels + ImGui::Dummy(ImVec2(availW, barH + 20)); + ImGui::SameLine(); + ImGui::NewLine(); + + for (size_t ch = 0; ch < nCh; ++ch) + { + float raw = masterVU[ch]; + + // smoothing & peak-hold + smooth[ch] = 0.1f * raw + 0.9f * smooth[ch]; + peakHold[ch] = std::max(peakHold[ch], smooth[ch]); + peakHold[ch] = std::max(0.0f, peakHold[ch] - decay[ch]); + + // clamp & normalized value + float v = std::clamp(smooth[ch], 0.0f, 1.0f); + bool clip = (v >= 1.0f); + + // compute dB for display + float amp = std::max(v, 1e-5f); + float dB = 20.0f * log10f(amp); + float norm = std::clamp((dB + 80.0f)/80.0f, 0.0f, 1.0f); + + // choose solid color by dBFS thresholds + ImU32 fillCol; + if (dB <= -12.0f) { + fillCol = IM_COL32(0,255,0,255); // green :contentReference[oaicite:8]{index=8} + } else if (dB <= -2.0f) { + fillCol = IM_COL32(255,255,0,255); // yellow :contentReference[oaicite:9]{index=9} + } else { + fillCol = IM_COL32(255,0,0,255); // red (warning) :contentReference[oaicite:10]{index=10} + } + clip = (dB >= 0.0f); // true clipping at ≥0 dBFS :contentReference[oaicite:11]{index=11} + ImU32 bgCol = IM_COL32(50,50,50,200); + + // compute bar rectangle (left-aligned) + float x0 = origin.x + ch * (barW + space); + float y0 = origin.y; + float x1 = x0 + barW; + float y1 = y0 + barH; + + // drop-shadow + dl->AddRectFilled({x0+2, y0+2}, {x1+2, y1+2}, IM_COL32(0,0,0,80), 3.0f); + // background (rounded corners) + dl->AddRectFilled({x0, y0}, {x1, y1}, bgCol, 3.0f); + // solid level fill + float fy = y1 - v*barH; + dl->AddRectFilled({x0, fy}, {x1, y1}, fillCol, 3.0f); + // peak-hold line + float py = y1 - peakHold[ch] * barH; + dl->AddLine({x0, py}, {x1, py}, IM_COL32(255,255,255,180), 2.0f); + if (clip) + dl->AddCircleFilled({x0 + barW*0.5f, y0 - 6}, 4.0f, IM_COL32(255,0,0,255)); + + char buf[16]; + snprintf(buf, sizeof(buf), "CH%zu", ch+1); + float tx = x0 + (barW - ImGui::CalcTextSize(buf).x)*0.5f; + dl->AddText({tx, y1 + 2}, IM_COL32(200,200,200,255), buf); + + snprintf(buf, sizeof(buf), "%.0fdb", dB); + tx = x0 + (barW - ImGui::CalcTextSize(buf).x)*0.5f; + dl->AddText({tx, y1 + 14}, IM_COL32(255,255,255,200), buf); + } + + ImGui::End(); +} diff --git a/src/src/editor/windows/AudioInfo.h b/src/src/editor/windows/AudioInfo.h new file mode 100644 index 0000000..c3a3188 --- /dev/null +++ b/src/src/editor/windows/AudioInfo.h @@ -0,0 +1,5 @@ +#pragma once + + +// Draw the Audio info window thing +void DrawAudioPlayingList(); \ No newline at end of file diff --git a/src/src/editor/windows/Inspector.cpp b/src/src/editor/windows/Inspector.cpp index 74abbb2..3fb6a56 100644 --- a/src/src/editor/windows/Inspector.cpp +++ b/src/src/editor/windows/Inspector.cpp @@ -747,6 +747,10 @@ void DrawInspectorUI(std::shared_ptr selected) static float volume = 1.0f; if (ImGui::SliderFloat("Volume", &volume, 0.0f, 1.0f)) audio->SetVolume(volume); + + + if (ImGui::Button("Remove AudioPlayerComponent")) + selected->RemoveComponent(); } ImGui::End(); diff --git a/src/vendor/miniaudio/miniaudio.c b/src/vendor/miniaudio/miniaudio.c new file mode 100644 index 0000000..6f88a61 --- /dev/null +++ b/src/vendor/miniaudio/miniaudio.c @@ -0,0 +1,7 @@ +#define MINIAUDIO_IMPLEMENTATION +#define MA_ENABLE_MP3 +#define MA_ENABLE_WAV +#define MA_ENABLE_FLAC +#define MA_ENABLE_VORBIS +#define MA_ENABLE_NODE_GRAPH +#include "miniaudio.h"