前置内容在上一篇【C++】QT | 音乐播放器初级
加载文件需要文件相关,而lrc文件适合用map容器匹配,用计时器来更新歌词:
#include <QMap> // map容器
#include <QFile> // 文件相关
#include <QTimer>
public:
void ReadLRC(const QString & filepath); // 读取对应路径的歌词文件
public slots:
void SetLRC(); // 槽函数,更新歌词
private:
std::map<long long, QString> LRCMap; // map容器,存储歌词:<时间, 歌词>
QTimer timer; // 设置定时器
在ui中增加label_LRC,并调节大小。
读取歌词文件:
ReadLRC("E:\\252QT\\202401\\Test\\music\\千百度.lrc");
......
long long StrToMsec(const QString & timeStr) // 传入QString对象,传回时间(毫秒)
{
auto leftList = timeStr.split(":"); // 先分出分钟
if(leftList.size() != 2) // 没有成功分割为两段时退出
return 0;
auto Min = leftList[0].toInt(); // 字符第一段转为数字,即分钟
auto rightList = leftList[1].split("."); // 剩下的字符按.分割
if(rightList.size() != 2) // 失败退出
return 0;
int sec = rightList[0].toInt(); // .第一段即秒
int msec = rightList[1].toInt(); // 第二段即毫秒
return msec + 1000 * sec + 1000 * 60 * Min; // 总毫秒
}
void Widget::ReadLRC(const QString & filepath) // 读取歌词文件
{
LRCMap.clear(); // 重置路径
QFile file(filepath);
if(file.open(QIODevice::ReadOnly) == true) // 以只读打开
{
QTextStream input(&file); // 读入文件流
while(input.atEnd() != true) // 不在末尾
{
QString content = input.readLine(); // 读取一行
QStringList LRCList = content.split("]"); // 分割时间与歌词
if(LRCList.size() != 2)
continue;
QString TimeString = LRCList[0].remove("["); // 提取时间字符串
long long Time = StrToMsec(TimeString); // 获取对应毫秒数
QString LRCString = LRCList[1];
LRCMap[Time] = LRCString; // 存入容器
}
file.close();
}
else
{
qDebug() << "文件不存在\n";
return;
}
}
加载音乐文件:
p.setMedia(QUrl::fromLocalFile("E:\\252QT\\202401\\Test\\music\\千百度.mp3"));
将定时器与歌词更新绑定:
connect(&timer, &QTimer::timeout, this, &Widget::SetLRC);
......
void Widget::SetLRC()
{
auto Time = p.position(); // 获取当前的音乐进度
for(auto ite = LRCMap.begin(); ite != LRCMap.end(); ) // 遍历LRC容器
{
auto PrevIte = ite++; // PrevIte接受ite的值,而ite向下
// 当ite未越界,取两个时间段之间的元素
if(ite != LRCMap.end() && PrevIte->first <= Time && ite->first > Time)
{
ui->label_LRC->setText(PrevIte->second); // 设置该标签为对应歌词
}
}
}
在播放函数中添加定时器更新:
void Widget::PlayMusic()
{
if(p.state() != QMediaPlayer::PlayingState)
{
p.play();
timer.start(200);
ui->pushButton_play->setIcon(QIcon(":/image/pause.png"));
}
else
{
p.pause();
timer.stop();
ui->pushButton_play->setIcon(QIcon(":/image/play.png"));
}
}
此时编译运行,即可看到播放时出现歌词。
文件
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QMediaPlayer>
#include <QMap> // map容器
#include <QFile> // 文件相关
#include <QTimer>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = nullptr);
~Widget();
void ReadLRC(const QString & filepath);
public slots:
void DurationChanged();
void PlayMusic();
void PositionChanged();
void SetLRC();
private:
Ui::Widget *ui;
QMediaPlayer p;
std::map<long long, QString> LRCMap;
QTimer timer;
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget)
{
ui->setupUi(this);
this->setWindowTitle("Test"); // 标题
this->setWindowIcon(QIcon(":/image/player.png")); // 应用图标
ui->pushButton_play->setIcon(QIcon(":/image/play.png")); // ui中该按钮设置图标
ReadLRC("E:\\Test\\music\\千百度.lrc");
p.setMedia(QUrl::fromLocalFile("E:\\Test\\music\\千百度.mp3"));
// 歌曲加载完毕,参数修改
connect(&p, &QMediaPlayer::durationChanged, this, &Widget::DurationChanged);
// 将pushButton_play按钮的clicked动作与this对象的PlayMusic方法绑定
connect(ui->pushButton_play, &QPushButton::clicked, this, &Widget::PlayMusic);
connect(&p, &QMediaPlayer::positionChanged, this, &Widget::PositionChanged);
connect(ui->horizontalSlider, &QSlider::sliderMoved, &p, &QMediaPlayer::setPosition);
connect(&timer, &QTimer::timeout, this, &Widget::SetLRC);
}
Widget::~Widget()
{
delete ui;
}
long long StrToMsec(const QString & timeStr) // 传入QString对象,传回时间(毫秒)
{
auto leftList = timeStr.split(":"); // 先分出分钟
if(leftList.size() != 2) // 没有成功分割为两段时退出
return 0;
auto Min = leftList[0].toInt(); // 字符第一段转为数字,即分钟
auto rightList = leftList[1].split("."); // 剩下的字符按.分割
if(rightList.size() != 2) // 失败退出
return 0;
int sec = rightList[0].toInt(); // .第一段即秒
int msec = rightList[1].toInt(); // 第二段即毫秒
return msec + 1000 * sec + 1000 * 60 * Min; // 总毫秒
}
void Widget::ReadLRC(const QString & filepath) // 读取歌词文件
{
LRCMap.clear(); // 重置路径
QFile file(filepath);
if(file.open(QIODevice::ReadOnly) == true) // 以只读打开
{
QTextStream input(&file); // 读入文件流
while(input.atEnd() != true) // 不在末尾
{
QString content = input.readLine(); // 读取一行
QStringList LRCList = content.split("]"); // 分割时间与歌词
if(LRCList.size() != 2)
continue;
QString TimeString = LRCList[0].remove("["); // 提取时间字符串
long long Time = StrToMsec(TimeString); // 获取对应毫秒数
QString LRCString = LRCList[1];
LRCMap[Time] = LRCString; // 存入容器
}
file.close();
}
else
{
qDebug() << "文件不存在\n";
return;
}
}
void Widget::DurationChanged()
{
long long duration = p.duration();
QString Time = QString("%1:%2").arg(duration / 1000 / 60).arg(duration / 1000 % 60);
ui->label_MusicTime->setText(Time);
ui->horizontalSlider->setRange(0, static_cast<int>(duration));
}
void Widget::PlayMusic()
{
if(p.state() != QMediaPlayer::PlayingState)
{
p.play();
timer.start(200);
ui->pushButton_play->setIcon(QIcon(":/image/pause.png"));
}
else
{
p.pause();
timer.stop();
ui->pushButton_play->setIcon(QIcon(":/image/play.png"));
}
}
void Widget::PositionChanged()
{
long long position = p.position();
QString Time = QString("%1:%2").arg(position / 1000 / 60).arg(position / 1000 % 60);
ui->label_CurrentTime->setText(Time);
ui->horizontalSlider->setValue(static_cast<int>(position));
}
void Widget::SetLRC()
{
auto Time = p.position(); // 获取当前的音乐进度
for(auto ite = LRCMap.begin(); ite != LRCMap.end(); ) // 遍历LRC容器
{
auto PrevIte = ite++; // PrevIte接受ite的值,而ite向下
// 当ite未越界,取两个时间段之间的元素
if(ite != LRCMap.end() && PrevIte->first <= Time && ite->first > Time)
{
ui->label_LRC->setText(PrevIte->second); // 设置该标签为对应歌词
}
}
}