From 44a947163d70036abaa7fc5ea8ff628d7cf22848 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Fri, 20 Feb 2026 20:34:06 -0800 Subject: [PATCH] Smooth login music start and lower auth-screen volume Add configurable fade-in support to MusicManager playback paths and use it for auth/login intro tracks to avoid abrupt starts. Apply a login-only 20% music attenuation while the auth screen is active, then restore the previous music volume when leaving login so in-game volume remains unchanged. --- include/audio/music_manager.hpp | 9 +++- include/ui/auth_screen.hpp | 2 + src/audio/music_manager.cpp | 78 +++++++++++++++++++++++++++------ src/ui/auth_screen.cpp | 21 +++++++-- 4 files changed, 90 insertions(+), 20 deletions(-) diff --git a/include/audio/music_manager.hpp b/include/audio/music_manager.hpp index aa5167bc..9a5bceea 100644 --- a/include/audio/music_manager.hpp +++ b/include/audio/music_manager.hpp @@ -18,8 +18,8 @@ public: bool initialize(pipeline::AssetManager* assets); void shutdown(); - void playMusic(const std::string& mpqPath, bool loop = true); - void playFilePath(const std::string& filePath, bool loop = true); + void playMusic(const std::string& mpqPath, bool loop = true, float fadeInMs = 0.0f); + void playFilePath(const std::string& filePath, bool loop = true, float fadeInMs = 0.0f); void stopMusic(float fadeMs = 2000.0f); void crossfadeTo(const std::string& mpqPath, float fadeMs = 3000.0f); void crossfadeToFile(const std::string& filePath, float fadeMs = 3000.0f); @@ -34,6 +34,7 @@ public: const std::string& getCurrentTrack() const { return currentTrack; } private: + float effectiveMusicVolume() const; pipeline::AssetManager* assetManager = nullptr; std::string currentTrack; bool currentTrackIsFile = false; @@ -47,6 +48,10 @@ private: bool pendingIsFile = false; float fadeTimer = 0.0f; float fadeDuration = 0.0f; + bool fadingIn = false; + float fadeInTimer = 0.0f; + float fadeInDuration = 0.0f; + float fadeInTargetVolume = 0.0f; std::unordered_map> musicDataCache_; }; diff --git a/include/ui/auth_screen.hpp b/include/ui/auth_screen.hpp index af1088af..80ada9fa 100644 --- a/include/ui/auth_screen.hpp +++ b/include/ui/auth_screen.hpp @@ -109,6 +109,8 @@ private: bool musicInitAttempted = false; bool musicPlaying = false; + bool loginMusicVolumeAdjusted_ = false; + int savedMusicVolume_ = 30; }; }} // namespace wowee::ui diff --git a/src/audio/music_manager.cpp b/src/audio/music_manager.cpp index 93f55132..8ccff4f2 100644 --- a/src/audio/music_manager.cpp +++ b/src/audio/music_manager.cpp @@ -2,6 +2,7 @@ #include "audio/audio_engine.hpp" #include "pipeline/asset_manager.hpp" #include "core/logger.hpp" +#include #include #include @@ -20,9 +21,21 @@ bool MusicManager::initialize(pipeline::AssetManager* assets) { return true; } +float MusicManager::effectiveMusicVolume() const { + float vol = volumePercent / 100.0f; + if (underwaterMode) { + vol *= 0.3f; + } + return vol; +} + void MusicManager::shutdown() { AudioEngine::instance().stopMusic(); playing = false; + fadingIn = false; + fadeInTimer = 0.0f; + fadeInDuration = 0.0f; + fadeInTargetVolume = 0.0f; currentTrack.clear(); musicDataCache_.clear(); } @@ -37,7 +50,7 @@ void MusicManager::preloadMusic(const std::string& mpqPath) { } } -void MusicManager::playMusic(const std::string& mpqPath, bool loop) { +void MusicManager::playMusic(const std::string& mpqPath, bool loop, float fadeInMs) { if (!assetManager) return; if (mpqPath == currentTrack && playing) return; @@ -59,9 +72,18 @@ void MusicManager::playMusic(const std::string& mpqPath, bool loop) { } // Play with AudioEngine (non-blocking, streams from memory) - float volume = volumePercent / 100.0f; - if (AudioEngine::instance().playMusic(cacheIt->second, volume, loop)) { + float targetVolume = effectiveMusicVolume(); + float startVolume = (fadeInMs > 0.0f) ? 0.0f : targetVolume; + if (AudioEngine::instance().playMusic(cacheIt->second, startVolume, loop)) { playing = true; + fadingIn = false; + if (fadeInMs > 0.0f) { + fadingIn = true; + fadeInTimer = 0.0f; + fadeInDuration = std::max(0.05f, fadeInMs / 1000.0f); + fadeInTargetVolume = targetVolume; + AudioEngine::instance().setMusicVolume(0.0f); + } currentTrack = mpqPath; currentTrackIsFile = false; LOG_INFO("Music: Playing ", mpqPath); @@ -70,7 +92,7 @@ void MusicManager::playMusic(const std::string& mpqPath, bool loop) { } } -void MusicManager::playFilePath(const std::string& filePath, bool loop) { +void MusicManager::playFilePath(const std::string& filePath, bool loop, float fadeInMs) { if (filePath.empty()) return; if (filePath == currentTrack && playing) return; if (!std::filesystem::exists(filePath)) { @@ -101,9 +123,18 @@ void MusicManager::playFilePath(const std::string& filePath, bool loop) { } // Play with AudioEngine - float volume = volumePercent / 100.0f; - if (AudioEngine::instance().playMusic(data, volume, loop)) { + float targetVolume = effectiveMusicVolume(); + float startVolume = (fadeInMs > 0.0f) ? 0.0f : targetVolume; + if (AudioEngine::instance().playMusic(data, startVolume, loop)) { playing = true; + fadingIn = false; + if (fadeInMs > 0.0f) { + fadingIn = true; + fadeInTimer = 0.0f; + fadeInDuration = std::max(0.05f, fadeInMs / 1000.0f); + fadeInTargetVolume = targetVolume; + AudioEngine::instance().setMusicVolume(0.0f); + } currentTrack = filePath; currentTrackIsFile = true; LOG_INFO("Music: Playing file ", filePath); @@ -116,6 +147,10 @@ void MusicManager::stopMusic(float fadeMs) { (void)fadeMs; // Fade not implemented yet AudioEngine::instance().stopMusic(); playing = false; + fadingIn = false; + fadeInTimer = 0.0f; + fadeInDuration = 0.0f; + fadeInTargetVolume = 0.0f; currentTrack.clear(); currentTrackIsFile = false; } @@ -127,11 +162,14 @@ void MusicManager::setVolume(int volume) { volumePercent = volume; // Update AudioEngine music volume directly (no restart needed!) - float vol = volumePercent / 100.0f; - if (underwaterMode) { - vol *= 0.3f; // 30% volume underwater + float vol = effectiveMusicVolume(); + if (fadingIn) { + fadeInTargetVolume = vol; + float t = std::clamp(fadeInTimer / std::max(fadeInDuration, 0.001f), 0.0f, 1.0f); + AudioEngine::instance().setMusicVolume(fadeInTargetVolume * t); + } else { + AudioEngine::instance().setMusicVolume(vol); } - AudioEngine::instance().setMusicVolume(vol); } void MusicManager::setUnderwaterMode(bool underwater) { @@ -139,11 +177,14 @@ void MusicManager::setUnderwaterMode(bool underwater) { underwaterMode = underwater; // Apply volume change immediately - float vol = volumePercent / 100.0f; - if (underwaterMode) { - vol *= 0.3f; // Fade to 30% underwater + float vol = effectiveMusicVolume(); + if (fadingIn) { + fadeInTargetVolume = vol; + float t = std::clamp(fadeInTimer / std::max(fadeInDuration, 0.001f), 0.0f, 1.0f); + AudioEngine::instance().setMusicVolume(fadeInTargetVolume * t); + } else { + AudioEngine::instance().setMusicVolume(vol); } - AudioEngine::instance().setMusicVolume(vol); } void MusicManager::crossfadeTo(const std::string& mpqPath, float fadeMs) { @@ -183,6 +224,15 @@ void MusicManager::update(float deltaTime) { playing = false; } + if (fadingIn) { + fadeInTimer += deltaTime; + float t = std::clamp(fadeInTimer / std::max(fadeInDuration, 0.001f), 0.0f, 1.0f); + AudioEngine::instance().setMusicVolume(fadeInTargetVolume * t); + if (t >= 1.0f) { + fadingIn = false; + } + } + // Handle crossfade if (crossfading) { fadeTimer += deltaTime; diff --git a/src/ui/auth_screen.cpp b/src/ui/auth_screen.cpp index 2501d4f2..9be570f3 100644 --- a/src/ui/auth_screen.cpp +++ b/src/ui/auth_screen.cpp @@ -211,6 +211,14 @@ void AuthScreen::render(auth::AuthHandler& authHandler) { if (renderer) { auto* music = renderer->getMusicManager(); if (music) { + if (!loginMusicVolumeAdjusted_) { + savedMusicVolume_ = music->getVolume(); + int loginVolume = (savedMusicVolume_ * 80) / 100; // reduce auth music by 20% + if (loginVolume < 0) loginVolume = 0; + if (loginVolume > 100) loginVolume = 100; + music->setVolume(loginVolume); + loginMusicVolumeAdjusted_ = true; + } music->update(ImGui::GetIO().DeltaTime); if (!music->isPlaying()) { static std::mt19937 rng(std::random_device{}()); @@ -253,7 +261,7 @@ void AuthScreen::render(auth::AuthHandler& authHandler) { if (!availableTracks.empty()) { std::uniform_int_distribution pick(0, availableTracks.size() - 1); const std::string& path = availableTracks[pick(rng)]; - music->playFilePath(path, true); + music->playFilePath(path, true, 1800.0f); LOG_INFO("AuthScreen: Playing login intro track: ", path); musicPlaying = music->isPlaying(); } else { @@ -477,14 +485,19 @@ void AuthScreen::render(auth::AuthHandler& authHandler) { } void AuthScreen::stopLoginMusic() { - if (!musicPlaying) return; auto& app = core::Application::getInstance(); auto* renderer = app.getRenderer(); if (!renderer) return; auto* music = renderer->getMusicManager(); if (!music) return; - music->stopMusic(500.0f); - musicPlaying = false; + if (musicPlaying) { + music->stopMusic(500.0f); + musicPlaying = false; + } + if (loginMusicVolumeAdjusted_) { + music->setVolume(savedMusicVolume_); + loginMusicVolumeAdjusted_ = false; + } } void AuthScreen::attemptAuth(auth::AuthHandler& authHandler) {