// 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 #include #include #include #include #include #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 video file (default: %s)\n" " --music audio file (default: %s)\n" " --lyrics .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* 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 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; }