feat: add backgroundDisplay for play ascii video in terminal feat: add musicPlayer for play music in terminal
147 lines
5.1 KiB
C++
147 lines
5.1 KiB
C++
// 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;
|
|
}
|