refactor(audio,transitions): durada de música pre-calculada al loadMusic i Direction (FADE_IN/FADE_OUT) a FadeToBlack
This commit is contained in:
1 parent
b6d6ee471c
commit
3f09858203
6 files changed
+58
-10
No files matched your search
@@ -194,6 +194,14 @@ void Audio::setOnMusicEnded(std::function<void()> callback) {
|
|||||||
if (engine_) { engine_->setOnMusicEnded(std::move(callback)); }
|
if (engine_) { engine_->setOnMusicEnded(std::move(callback)); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Resol el nom contra el cache de recursos i retorna la durada pre-calculada
|
||||||
|
// al `loadMusic`. 0 si la pista no existeix — així el caller pot decidir
|
||||||
|
// fallback (p. ex. usar un timeout fix) sense haver de propagar errors.
|
||||||
|
auto Audio::getMusicDurationMs(const std::string& name) -> int {
|
||||||
|
auto* music = AudioResource::getMusic(name);
|
||||||
|
return (music != nullptr) ? music->duration_ms : 0;
|
||||||
|
}
|
||||||
|
|
||||||
// Consulta directament l'estat a Ja i el projecta al subconjunt d'estats que
|
// Consulta directament l'estat a Ja i el projecta al subconjunt d'estats que
|
||||||
// exposa Audio (INVALID/DISABLED de Ja col·lapsen a STOPPED — la capa d'usuari
|
// exposa Audio (INVALID/DISABLED de Ja col·lapsen a STOPPED — la capa d'usuari
|
||||||
// només vol saber si està sonant, pausat o parat).
|
// només vol saber si està sonant, pausat o parat).
|
||||||
|
|||||||
@@ -121,6 +121,11 @@ class Audio {
|
|||||||
[[nodiscard]] auto isMusicEnabled() const -> bool { return music_enabled_; }
|
[[nodiscard]] auto isMusicEnabled() const -> bool { return music_enabled_; }
|
||||||
[[nodiscard]] static auto getMusicState() -> MusicState; // Estat real consultat a Ja::
|
[[nodiscard]] static auto getMusicState() -> MusicState; // Estat real consultat a Ja::
|
||||||
[[nodiscard]] auto getCurrentMusicName() const -> const std::string& { return music_.name; }
|
[[nodiscard]] auto getCurrentMusicName() const -> const std::string& { return music_.name; }
|
||||||
|
// Durada de la pista resolta per nom (mil·lisegons). 0 si la pista no
|
||||||
|
// existeix al cache de recursos o si el seu header OGG no permet
|
||||||
|
// calcular-la. Pensat per a clients que necessiten un timeline
|
||||||
|
// determinista (p. ex. RoomFsm) sense dependre de callbacks de fi.
|
||||||
|
[[nodiscard]] static auto getMusicDurationMs(const std::string& name) -> int;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// --- Tipus anidats ---
|
// --- Tipus anidats ---
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <cstdint>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
@@ -564,6 +565,15 @@ namespace Ja {
|
|||||||
music->spec.channels = static_cast<int>(INFO.channels);
|
music->spec.channels = static_cast<int>(INFO.channels);
|
||||||
music->spec.freq = static_cast<int>(INFO.sample_rate);
|
music->spec.freq = static_cast<int>(INFO.sample_rate);
|
||||||
music->spec.format = SDL_AUDIO_S16;
|
music->spec.format = SDL_AUDIO_S16;
|
||||||
|
// Pre-càlcul de la durada en ms a partir del header. stb_vorbis ja
|
||||||
|
// ha decodificat la informació necessària a `stb_vorbis_open_memory`;
|
||||||
|
// aquesta consulta no descodifica àudio, només llig el comptador
|
||||||
|
// de samples. Si el sample_rate fos 0 (header malmès) deixem
|
||||||
|
// duration_ms a 0.
|
||||||
|
if (INFO.sample_rate > 0) {
|
||||||
|
const auto SAMPLES = stb_vorbis_stream_length_in_samples(music->vorbis);
|
||||||
|
music->duration_ms = static_cast<int>((static_cast<std::uint64_t>(SAMPLES) * 1000ULL) / INFO.sample_rate);
|
||||||
|
}
|
||||||
music->state = MusicState::STOPPED;
|
music->state = MusicState::STOPPED;
|
||||||
|
|
||||||
return music.release();
|
return music.release();
|
||||||
|
|||||||
@@ -112,6 +112,12 @@ namespace Ja {
|
|||||||
std::string filename;
|
std::string filename;
|
||||||
|
|
||||||
int times{0}; // loops restants (-1 = infinit, 0 = un sol play)
|
int times{0}; // loops restants (-1 = infinit, 0 = un sol play)
|
||||||
|
// Durada total de la pista en mil·lisegons, mesurada via
|
||||||
|
// `stb_vorbis_stream_length_in_samples / sample_rate` al
|
||||||
|
// `loadMusic`. 0 si el càlcul no és possible (header malmès).
|
||||||
|
// L'usen consumidors que necessiten un timeline pre-calculat —
|
||||||
|
// p. ex. la FSM de sala — sense dependre de callbacks de fi.
|
||||||
|
int duration_ms{0};
|
||||||
SDL_AudioStream* stream{nullptr};
|
SDL_AudioStream* stream{nullptr};
|
||||||
MusicState state{MusicState::INVALID};
|
MusicState state{MusicState::INVALID};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,9 +8,10 @@
|
|||||||
|
|
||||||
namespace Transitions {
|
namespace Transitions {
|
||||||
|
|
||||||
void FadeToBlack::begin(int duration_ms) {
|
void FadeToBlack::begin(int duration_ms, Direction direction) {
|
||||||
duration_ms_ = duration_ms;
|
duration_ms_ = duration_ms;
|
||||||
elapsed_ms_ = 0;
|
elapsed_ms_ = 0;
|
||||||
|
direction_ = direction;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FadeToBlack::tick(int delta_ms) {
|
void FadeToBlack::tick(int delta_ms) {
|
||||||
@@ -29,7 +30,11 @@ namespace Transitions {
|
|||||||
// Alpha lineal — sense corba quadràtica (això és prerogativa del
|
// Alpha lineal — sense corba quadràtica (això és prerogativa del
|
||||||
// dissolve mosaic, on volem que la mascara fosca arribe tard
|
// dissolve mosaic, on volem que la mascara fosca arribe tard
|
||||||
// perquè es vegen els blocs). Per al fade net, lineal 0..255.
|
// perquè es vegen els blocs). Per al fade net, lineal 0..255.
|
||||||
const auto ALPHA = static_cast<std::uint8_t>(std::round(255.0F * P));
|
// Direcció FADE_IN inverteix la rampa: el negre comença opac i
|
||||||
|
// s'esvaeix cap a transparent, deixant veure l'escena pintada
|
||||||
|
// darrere abans de `render()`.
|
||||||
|
const float ALPHA_F = (direction_ == Direction::FADE_IN) ? (1.0F - P) : P;
|
||||||
|
const auto ALPHA = static_cast<std::uint8_t>(std::round(255.0F * ALPHA_F));
|
||||||
if (ALPHA == 0) { return; }
|
if (ALPHA == 0) { return; }
|
||||||
|
|
||||||
auto& screen = Screen::get();
|
auto& screen = Screen::get();
|
||||||
|
|||||||
@@ -1,37 +1,51 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
namespace Transitions {
|
namespace Transitions {
|
||||||
|
|
||||||
// Fade-out lineal a negre. A diferència de `Mosaic`, no rebina cap
|
// Fade lineal contra negre. A diferència de `Mosaic`, no rebina cap
|
||||||
// píxel; només posa una capa negra amb alpha creixent (0 → 255) al
|
// píxel; només posa una capa negra al damunt del canvas amb alpha
|
||||||
// damunt del canvas. És la transició "neta" per defecte; ideal per a
|
// que evoluciona linealment al llarg de la durada. És la transició
|
||||||
// sortides on no convé l'estètica chunky de l'arcade dissolve.
|
// "neta" per defecte; ideal per a entrades/sortides on no convé
|
||||||
|
// l'estètica chunky de l'arcade dissolve.
|
||||||
|
//
|
||||||
|
// La direcció controla el sentit de l'alpha:
|
||||||
|
// FADE_OUT: alpha 0 → 255 (món visible → negre opac)
|
||||||
|
// FADE_IN: alpha 255 → 0 (negre opac → món visible)
|
||||||
|
// En tots dos casos `progress()` segueix sent monotònic 0 → 1
|
||||||
|
// (mateixa semàntica per al caller); només el render inverteix.
|
||||||
//
|
//
|
||||||
// Mateixa interfície que `Mosaic` (begin/tick/render/isActive/
|
// Mateixa interfície que `Mosaic` (begin/tick/render/isActive/
|
||||||
// isFinished/progress) perquè els llocs de crida puguen escollir-ne
|
// isFinished/progress) perquè els llocs de crida puguen escollir-ne
|
||||||
// una o l'altra sense canviar el patró d'ús.
|
// una o l'altra sense canviar el patró d'ús.
|
||||||
class FadeToBlack {
|
class FadeToBlack {
|
||||||
public:
|
public:
|
||||||
|
enum class Direction : std::uint8_t { FADE_OUT,
|
||||||
|
FADE_IN };
|
||||||
|
|
||||||
FadeToBlack() = default;
|
FadeToBlack() = default;
|
||||||
|
|
||||||
// Arrenca un fade nou. Si ja n'hi havia un actiu, el reemplaça.
|
// Arrenca un fade nou. Si ja n'hi havia un actiu, el reemplaça.
|
||||||
// `duration_ms <= 0` deixa l'efecte inactiu.
|
// `duration_ms <= 0` deixa l'efecte inactiu.
|
||||||
void begin(int duration_ms);
|
void begin(int duration_ms, Direction direction = Direction::FADE_OUT);
|
||||||
|
|
||||||
void tick(int delta_ms);
|
void tick(int delta_ms);
|
||||||
|
|
||||||
[[nodiscard]] auto isActive() const -> bool { return duration_ms_ > 0 && elapsed_ms_ < duration_ms_; }
|
[[nodiscard]] auto isActive() const -> bool { return duration_ms_ > 0 && elapsed_ms_ < duration_ms_; }
|
||||||
[[nodiscard]] auto isFinished() const -> bool { return duration_ms_ > 0 && elapsed_ms_ >= duration_ms_; }
|
[[nodiscard]] auto isFinished() const -> bool { return duration_ms_ > 0 && elapsed_ms_ >= duration_ms_; }
|
||||||
[[nodiscard]] auto progress() const -> float;
|
[[nodiscard]] auto progress() const -> float;
|
||||||
|
[[nodiscard]] auto direction() const -> Direction { return direction_; }
|
||||||
|
|
||||||
// Pinta la capa negra amb alpha proporcional al `progress()`.
|
// Pinta la capa negra amb alpha derivat del `progress()` i la
|
||||||
// Cridar després del render normal de l'escena, abans de
|
// direcció. Cridar després del render normal de l'escena,
|
||||||
// `Screen::present()`.
|
// abans de `Screen::present()`.
|
||||||
void render() const;
|
void render() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int duration_ms_{0};
|
int duration_ms_{0};
|
||||||
int elapsed_ms_{0};
|
int elapsed_ms_{0};
|
||||||
|
Direction direction_{Direction::FADE_OUT};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Transitions
|
} // namespace Transitions
|
||||||
Reference in new issue
Block a user