feat: add lyricResolve for resole .lrc file

feat: add backgroundDisplay for play ascii video in terminal
	feat: add musicPlayer for play music in terminal
This commit is contained in:
2026-08-30 11:35:47 +08:00
parent fdfbf5262f
commit 00b7c0b324
16 changed files with 97267 additions and 15 deletions
+146
View File
@@ -0,0 +1,146 @@
// tools/demo.cpp
//
// Driver that previews the three show modules together: an ASCII video
// background, a music track and timed lyrics. This is a preview harness for
// development — the real show will be driven by uninstall.cpp.
//
// The default resource paths are baked in at build time (CMake); every path
// can be overridden on the command line.
#include "backgroundDisplay.h"
#include "lyricResolve.h"
#include "musicPlayer.h"
#include <atomic>
#include <chrono>
#include <cstdio>
#include <cstdlib>
#include <string>
#include <thread>
#ifndef HMS_DEMO_VIDEO
#define HMS_DEMO_VIDEO "asciivision/demo-videos/demo.mp4"
#endif
#ifndef HMS_DEMO_MUSIC
#define HMS_DEMO_MUSIC "res/music.flac"
#endif
#ifndef HMS_DEMO_LYRICS
#define HMS_DEMO_LYRICS "res/lyric.lrc"
#endif
static void usage(const char* argv0) {
std::printf(
"Usage: %s [options]\n"
" --video <path> video file (default: %s)\n"
" --music <path> audio file (default: %s)\n"
" --lyrics <path> .lrc lyric file (default: %s)\n"
" --no-video skip the video\n"
" --no-music skip the music\n"
" --no-lyrics skip the lyrics\n"
" --no-resize force the 'terminal not adjustable' fit path\n"
" --seconds N stop the show after N seconds\n",
argv0, HMS_DEMO_VIDEO, HMS_DEMO_MUSIC, HMS_DEMO_LYRICS);
}
static void playMusicQuietly(const std::string& path, std::atomic<bool>* stop) {
const int rc = mp::play(path, stop);
if (rc == 2) {
std::fprintf(stderr, "demo: cannot open audio file '%s'\n", path.c_str());
} else if (rc == 3) {
std::fprintf(stderr, "demo: no audio device available - continuing without music\n");
}
}
int main(int argc, char** argv) {
std::string video = HMS_DEMO_VIDEO;
std::string music = HMS_DEMO_MUSIC;
std::string lyrics = HMS_DEMO_LYRICS;
bool useVideo = true, useMusic = true, useLyrics = true;
bool tryResize = true;
int seconds = 0;
for (int i = 1; i < argc; ++i) {
const std::string a = argv[i];
if (a == "--no-video") {
useVideo = false;
} else if (a == "--no-music") {
useMusic = false;
} else if (a == "--no-lyrics") {
useLyrics = false;
} else if (a == "--no-resize") {
tryResize = false;
} else if ((a == "--video" || a == "--music" || a == "--lyrics" || a == "--seconds") &&
i + 1 < argc) {
const std::string& value = argv[++i];
if (a == "--video") video = value;
else if (a == "--music") music = value;
else if (a == "--lyrics") lyrics = value;
else seconds = std::atoi(value.c_str());
} else if (a == "-h" || a == "--help") {
usage(argv[0]);
return 0;
} else {
usage(argv[0]);
return 1;
}
}
std::atomic<bool> stop{false};
// Optional hard deadline (e.g. for quick verification).
std::thread deadlineThread;
if (seconds > 0) {
deadlineThread = std::thread([&] {
std::this_thread::sleep_for(std::chrono::seconds(seconds));
stop.store(true);
});
}
if (!useVideo) {
// No video: just music + lyrics.
std::thread musicThread, lyricThread;
if (useMusic) musicThread = std::thread([&] { playMusicQuietly(music, &stop); });
if (useLyrics) lyricThread = std::thread([&] { lr::playLyricsFile(lyrics, {}, &stop); });
if (musicThread.joinable()) musicThread.join();
if (lyricThread.joinable()) lyricThread.join();
} else if (!useMusic && !useLyrics) {
// Only video: play once to the end.
bd::PlayVideoOptions vo;
vo.path = video;
vo.loop = false;
vo.tryResize = tryResize;
vo.stop = &stop;
bd::playVideo(video, vo);
} else {
// Full show: video loops in the background, music + lyrics on top.
// Reserve the bottom row for the lyric line.
std::thread videoThread([&] {
bd::PlayVideoOptions vo;
vo.path = video;
vo.loop = true;
vo.tryResize = tryResize;
vo.bottomRows = useLyrics ? 1 : 0;
vo.stop = &stop;
bd::playVideo(video, vo);
});
// Give the video a moment to detect and adjust the terminal size so
// the lyric row resolves against the final layout.
std::this_thread::sleep_for(std::chrono::milliseconds(400));
std::thread musicThread, lyricThread;
if (useMusic) musicThread = std::thread([&] { playMusicQuietly(music, &stop); });
if (useLyrics) lyricThread = std::thread([&] { lr::playLyricsFile(lyrics, {}, &stop); });
if (musicThread.joinable()) musicThread.join();
if (lyricThread.joinable()) lyricThread.join();
stop.store(true); // end the looping video
videoThread.join();
}
if (deadlineThread.joinable()) deadlineThread.join();
// Restore a clean screen.
std::printf("\x1b[0m\x1b[2J\x1b[H\x1b[?25h");
return 0;
}