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.
This commit is contained in:
Kelsi 2026-02-20 20:34:06 -08:00
parent 3368dbb9ec
commit 44a947163d
4 changed files with 90 additions and 20 deletions

View file

@ -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<std::string, std::vector<uint8_t>> musicDataCache_;
};

View file

@ -109,6 +109,8 @@ private:
bool musicInitAttempted = false;
bool musicPlaying = false;
bool loginMusicVolumeAdjusted_ = false;
int savedMusicVolume_ = 30;
};
}} // namespace wowee::ui

View file

@ -2,6 +2,7 @@
#include "audio/audio_engine.hpp"
#include "pipeline/asset_manager.hpp"
#include "core/logger.hpp"
#include <algorithm>
#include <filesystem>
#include <fstream>
@ -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;

View file

@ -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<size_t> 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) {