- 分享
lrc歌词命令行播放工具+wav播放(AI生成)
- @ 2025-12-6 10:41:26
加上以下编译参数(devc++可能不行,编译器太老了,至少c++17)
-lwinmm -std=c++17
lrc文件可以在笑匠音乐歌词库下载
wav可以下载bilibili视频,再转换为wav bilibili视频下载:bilibili视频下载器 mp4转wav:MP4转WAV
命令参数:music_player.exe test.wav test.lrc
#include <iostream>
#include <fstream>
#include <string>
#include <map>
#include <regex>
#include <chrono>
#include <thread>
// Windows核心头文件(PlaySound专属)
#ifdef _WIN32
#include <windows.h>
#include <mmsystem.h>
// PlaySound需要链接winmm.lib,MinGW编译时仍需加-lwinmm
#else
#include <cstdlib>
#endif
// 设置控制台编码为UTF-8
void setConsoleUtf8() {
#ifdef _WIN32
SetConsoleOutputCP(CP_UTF8);
SetConsoleCP(CP_UTF8);
#endif
}
// 跨平台清屏
void clearScreen() {
#ifdef _WIN32
system("cls");
#else
system("clear");
#endif
}
// 播放WAV音频(改用PlaySound,100%兼容)
bool playAudio(const std::string& audioPath) {
// 检查文件是否存在(先做前置校验)
std::ifstream file(audioPath, std::ios::binary);
if (!file.is_open()) {
std::cerr << "错误:音频文件不存在 \"" << audioPath << "\"" << std::endl;
return false;
}
file.close();
// PlaySound参数说明:
// SND_FILENAME:指定路径为文件名
// SND_ASYNC:异步播放(不阻塞歌词显示)
// SND_LOOP:循环播放(和之前的repeat效果一致)
BOOL ret = PlaySoundA(audioPath.c_str(), NULL, SND_FILENAME | SND_ASYNC | SND_LOOP);
if (!ret) {
std::cerr << "错误:无法播放WAV音频文件 \"" << audioPath << "\"" << std::endl;
std::cerr << "原因:WAV文件损坏/不是标准PCM格式WAV" << std::endl;
return false;
}
std::cout << "音频播放已启动:" << audioPath << std::endl;
return true;
}
// 停止音频播放
void stopAudio() {
PlaySoundA(NULL, NULL, 0); // 传入NULL停止所有播放
std::cout << "\n音频已停止播放" << std::endl;
}
// 时间字符串转毫秒
long long timeStrToMs(const std::string& timeStr) {
std::regex pattern(R"((\d+):(\d+)\.(\d+))");
std::smatch matches;
if (std::regex_match(timeStr, matches, pattern)) {
int minutes = std::stoi(matches[1]);
int seconds = std::stoi(matches[2]);
int milliseconds = std::stoi(matches[3]);
if (matches[3].length() == 1) milliseconds *= 100;
else if (matches[3].length() == 2) milliseconds *= 10;
return minutes * 60 * 1000 + seconds * 1000 + milliseconds;
}
return -1;
}
// 解析LRC文件
std::map<long long, std::string> parseLRC(const std::string& filename) {
std::map<long long, std::string> lrcMap;
std::ifstream file(filename, std::ios::binary);
if (!file.is_open()) {
std::cerr << "错误:无法打开歌词文件 \"" << filename << "\"" << std::endl;
std::cerr << "当前工作目录:";
#ifdef _WIN32
char buffer[MAX_PATH];
GetCurrentDirectoryA(MAX_PATH, buffer);
std::cerr << buffer << std::endl;
#endif
return lrcMap;
}
std::string content((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
file.close();
// 移除UTF-8 BOM
if (content.size() >= 3 && (unsigned char)content[0] == 0xEF &&
(unsigned char)content[1] == 0xBB && (unsigned char)content[2] == 0xBF) {
content = content.substr(3);
}
std::regex lineRegex(R"(\r?\n)");
std::sregex_token_iterator it(content.begin(), content.end(), lineRegex, -1);
std::sregex_token_iterator end;
std::regex linePattern(R"(\[(\d+:\d+\.\d+)\](.*))");
std::smatch matches;
for (; it != end; ++it) {
std::string line = *it;
if (line.empty()) continue;
if (std::regex_match(line, matches, linePattern)) {
std::string timeStr = matches[1];
std::string lyric = matches[2];
lyric = std::regex_replace(lyric, std::regex("^\\s+|\\s+$"), "");
long long ms = timeStrToMs(timeStr);
if (ms != -1 && !lyric.empty()) {
lrcMap[ms] = lyric;
}
}
}
std::cout << "成功解析到 " << lrcMap.size() << " 句歌词" << std::endl;
return lrcMap;
}
// 同步显示歌词
void playLyricsSync(const std::map<long long, std::string>& lrcMap) {
if (lrcMap.empty()) {
std::cout << "没有可显示的歌词" << std::endl;
return;
}
clearScreen();
long long currentTime = 0;
auto it = lrcMap.begin();
std::string lastLyric = "";
std::cout << "开始同步显示歌词(按Ctrl+C停止):\n" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1)); // 等音频启动
while (currentTime <= 5 * 60 * 1000 && it != lrcMap.end()) { // 最多5分钟
if (currentTime >= it->first) {
if (it->second != lastLyric) {
clearScreen();
std::cout << "==============================" << std::endl;
std::cout << "\t\t" << it->second << std::endl;
std::cout << "==============================" << std::endl;
std::cout << "\n播放进度:" << currentTime / 1000 << "秒" << std::endl;
lastLyric = it->second;
}
++it;
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
currentTime += 100;
}
}
int main(int argc, char* argv[]) {
setConsoleUtf8();
if(argc != 3) {
std::cout << "使用方法:music_player.exe 音频文件路径 歌词文件路径" << std::endl;
std::cout << "示例:music_player.exe test.wav test.lrc" << std::endl;
std::cout << "仅支持WAV格式(必须是标准PCM编码)" << std::endl;
return 1;
}
std::string audioFile = argv[1];
std::string lrcFile = argv[2];
std::cout << "当前程序运行目录:";
#ifdef _WIN32
char buffer[MAX_PATH];
GetCurrentDirectoryA(MAX_PATH, buffer);
std::cout << buffer << std::endl;
#endif
std::cout << "正在尝试打开音频:" << audioFile << std::endl;
std::cout << "正在尝试打开歌词:" << lrcFile << std::endl;
if (!playAudio(audioFile)) return 1;
auto lrcMap = parseLRC(lrcFile);
playLyricsSync(lrcMap);
stopAudio();
return 0;
}
1 comments
-
qsq LV 5 @ 2025-12-6 10:47:58
这是一个lrc的示例:
[00:03.952]ねえあたし知ってるよ きみがひとり“XX”してるの知ってるよ [00:08.408]ビクンビクン震えてさ 声もダダ漏れなんだわ [00:11.205]正直に言っちゃえよ バレてるんだし言っちゃえよ 効いてんの? [00:15.782]普通普通 恥ずかしい?みんな隠しているだけ [00:18.522] [00:25.828]ねえあたし知ってるよ きみがひとり“涙”してるの知ってるよ [00:30.294]グスングスン凹んでさ 弱音ヒトカラ in the night [00:33.063]朝が来るまで一緒コース もっと泣いたって [00:35.507]何度だって受け止めてあげる [00:37.778]もう我慢しないでいっぱい出してね [00:40.255] [00:40.311]MWAH! [00:41.395]お願い きみが欲しいの [00:43.497]慰めさせてシェイクシェイク 愛の才能で [00:47.131]泣いてくれなきゃ 涸れてしまう 濡れていたい [00:50.459]ねえいいでしょう? 舐め取って 飲み干したいんだってば [00:54.837] [00:54.838]MWAH! [00:55.898]お願い きみが欲しいの [00:58.087]頼り散らしてシックラブ なんて最高ね [01:01.666]分けてくれなきゃ 君の“痛い”感じていたい [01:05.049]ねえいいでしょう? 吸い取って 救いたいんだってば [01:09.708] [01:10.420]見たいの きみの中 [01:17.449] [01:23.925]ねえあたし知ってるよ きみがひとり悔しがってんの知ってるよ [01:28.437]ズキュンズキュン高まるじゃん きみを推すことをやめない [01:31.290]ねえあたし知ってるよ きみはできる子 知ってるよ [01:34.879]つらい時は弱いくらいで丁度いい あたしそれでも好きだよ [01:38.469] [01:38.470]MWAH! [01:39.521]お願い きみが欲しいの [01:41.772]名前を呼んでよ いつだって会いに参上 [01:45.391]きみはひとりだ だから歌う「ひとりじゃない」 [01:48.623]もういいでしょう [01:49.693]ソロプレイはお仕舞いなんだってば [01:53.545] [01:53.982]きみが病めるときも あたし側にいるわ [02:00.857]いつも見守っているわ そうよ 怖くないのよ [02:09.991] [02:11.295]MWAH! [02:12.214]お願い きみが欲しいの [02:14.465]慰めさせてシェイクシェイク 愛の才能で [02:18.091]泣いてくれなきゃ 涸れてしまう 濡れていたい [02:21.266]ねえいいでしょう? 舐め取って 飲み干したいんだってば [02:25.836] [02:25.837]MWAH! [02:26.815]お願い きみが欲しいの [02:29.007]頼り散らしてシックラブ なんて最高ね [02:32.659]分けてくれなきゃ 君の“痛い”感じていたい 覗いていたい [02:36.879]吸い取って 救いたいんだってば [02:40.328] [02:40.329]ねえあたし知ってるよ きみがひとり“涙”してるの知ってるよ [02:44.783]グスングスン凹んでさ 弱音ヒトカラ in the night [02:47.626]朝が来るまで一緒コース もっと泣いたって [02:50.136]何度だって受け止めてあげる [02:52.348]もう我慢しないで出してってば [02:54.768] [02:54.769]さあ [02:55.955]曲名:モニタリング lrc歌词来源
- 1