// lyricResolve.h // // Parse .lrc lyric files and play them back on a timeline in the terminal. // // playLyrics()/playLyricsFile() anchor their timeline at the moment they are // called (t = 0) and print each lyric line at its timestamp. The module is // self-contained and multibyte-safe: Japanese/Chinese UTF-8 lines are printed // whole and only ever truncated on a character boundary. #pragma once #include #include #include #include #include "timeline.h" namespace lr { struct LyricLine { int64_t timeMs = 0; // from the start of the track std::string text; // empty => clear the display }; struct LyricInfo { std::vector lines; // sorted ascending by timeMs (stable) std::string title, artist, album, by; int offsetMs = 0; bool valid = false; // false if nothing was parsed }; // Parse LRC text: "[mm:ss.xx]text", multiple timestamps per line, metadata // tags ([ti:]/[ar:]/[al:]/[by:]/[offset:]) and timestamp-only lines (empty // text, which clears the display). The [offset:] value is applied to all lines. LyricInfo parseLrc(const std::string& text); // Read a file and parse it. Returns a LyricInfo with valid == false on error. LyricInfo parseLrcFile(const std::string& path); struct LyricDisplayOptions { int row = 0; // 1-based terminal row; 0 = auto (last terminal row) int col = 0; // 1-based start column; 0 = column 1 int maxWidth = 0; // max display columns; 0 = terminal width - 1 bool show = true; // false => run the timeline silently (no terminal writes) }; // Play the lyrics against `timeline` (may be null for a local clock): the // current lyric line is displayed in real time as the timeline advances. // Blocks until the timeline passes the last line (or `stop` is set). // Returns 0. int playLyrics(const LyricInfo& lyric, const LyricDisplayOptions& opts = {}, Timeline* timeline = nullptr, std::atomic* stop = nullptr); int playLyricsFile(const std::string& path, const LyricDisplayOptions& opts = {}, Timeline* timeline = nullptr, std::atomic* stop = nullptr); } // namespace lr