QT音乐播放器
文章目录
前言
最近在搞语音识别,本来没打算做音乐播放器的,看了正点原子的语音识别章节,发现音乐播放器好像绕不开,后面还要弄个录音的系统,不服就干。趁现在知识点还热乎,赶紧记下笔记和心得。话不多说,showtime!!!
作品演示
电脑不太给力,有一段听着有点像2倍速,实际上代码没有这种情况的,这是录屏软件的锅,我的大神形象要毁于一旦了,呜呜呜!
音乐播放器
代码实现的几个关键 步骤
步骤总览
1…ui文件的布局
2.背景图片,包括widget和按钮的图片
3.进度条与音乐关联起来,进度条播放时间实时显示
4.歌词的显示
5.歌单的显示以及歌单列表和歌曲关联起来
6.一些细节部分的修修补补
ui文件的布局
这里比较简单,就不多说了
背景图片,包括widget和按钮的图片
按钮背景图片看下图:
在.ui文件中找到按钮->右键选中“改变样式表”->在“添加资源”中选中“border-image”->选中自己添加到资源文件夹中的图片->点击添加即可
widget背景图片代码:
QPixmap pix;
pix.load(":/new/prefix1/background.jpg");
this->setAutoFillBackground(true); ///一定要加这行
QPalette bgPalette = this->palette();
bgPalette.setBrush(QPalette::Background,QBrush(pix));
this->setPalette(bgPalette);
进度条与音乐关联起来,进度条播放时间实时显示
用到的两个槽函数
connect(musicPlayer, SIGNAL(durationChanged(qint64)), this, SLOT(musicPlayerDurationChanged(qint64)));
connect(musicPlayer,SIGNAL(positionChanged(qint64)),this,SLOT(onPositionChanged(qint64)));
信号量durationChanged,是musicPlayer发出的,它在音乐的开始和结束都会触发一次,参数就是该歌曲的时长了。
信号量positionChanged也是musicPlayer发出的,它在音乐时长的改变都会触发,参数就是歌曲走过的时长。
知道这个就很容易将歌曲和进度条关联起来了。
ui->horizontalSlider->setMaximum(duration); //设置进度条最大值 也就是歌曲时长 ms
int secs = duration/1000; //全部秒数
int mins = secs/60;//分
secs = secs % 60;//秒
durationTime = QString::asprintf("%d:%d",mins,secs);
我们可以获取歌曲的时长,ms级的,将时长赋值给进度条,进度条的总长度就有了
ui->horizontalSlider->setSliderPosition(position);
int secs = position/1000;
int mins = secs/60;
secs = secs % 60;
positionTime = QString::asprintf("%d:%d",mins,secs);
将当前歌曲播放的时长赋值给进度条,就相当于歌曲播放了多久,进度条就划过多远,通过上面的代码进度条就巧妙的和歌曲联系起来了。
歌词的显示
在还没接触到这个实验的时候,我天真的以为强大的QT会自动获取歌曲里边的歌曲,然后解析出来,只需要调用几个函数的事,哈哈哈,想的真美好。事实上,要显示歌词还得有一个后缀为.lvr的文件,用记事本打开是这样子的。可以一目了然,什么时候播放哪一句话。拿到这么一个文件之后我们就要对它截取,截取包括时间的截取和歌词的截取。用到正则表达式。我呢是从一位网友哪里下载来的文件,然后利用CV大法,嘻嘻嘻。为了表示感激后面我会把他的链接发出来的。
一起来看下这位网友的关键代码吧,看着一大堆,但其实也挺简单,用到的主要是QRegularExpression和QRegularExpressionMatch,后面的那些就是获取拿到的数据,只是将数据类型改变一下而已。然后把数据存入到Qlist里边。
bool Lyrices::analysisLyricsFile(QString line)
{
if(line == NULL || line.isEmpty()){
qDebug()<<"thie line is empty!";
return false;
}
QRegularExpression regularExpression("\\[(\\d+)?:(\\d+)?(\\.\\d+)?\\](.*)?");
QRegularExpressionMatch match;
match = regularExpression.match(line);
if(match.hasMatch()) {
int totalTime;
//利用正则表达式解析出来,获取用.captured(1) .captured(2) .captured(3) .captured(4)。。。方式获取
totalTime = match.captured(1).toInt() * 60000 + match.captured(2).toInt() * 1000; /* 计算该时间点毫秒数 */
QString currentText =QString::fromStdString(match.captured(4).toStdString()); /* 获取歌词文本*/
listLyricsText.push_back(currentText);
listLyricsTime.push_back(totalTime);
}
return false;
}
获取到了歌词和时间后,接下来就是实时显示歌词了,前面不是说到我们可以获取歌曲播放了多久嘛,那么我们为什么不可以用那个数据和我们获取的歌词时间数据做对比呢,不过歌词的数据刚好大于歌曲播放的时间,不就说明要显示的是那个时间点的那句歌词了吗?下面这个函数就是我用正则表达式获取到的数据后的处理。
bool Lyrices::readLyricsFile(QString lyricsPath,qint64 what_ms)
{
QFile file(lyricsPath);
if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){
listLyricsText.clear();
listLyricsTime.clear();
return false;
}
// 歌词一句一句获取出来,然后利用analysisLyricsFile解析保存到对应数组里
QString line="";
while((line=file.readLine())>0){
analysisLyricsFile(line);
}
// 对比我们传进来的what_ms时间参数,如果时间数组里边的数刚好大于我们传的参数,就跳出循环
for(int oo=0;oo<listLyricsTime.size();oo++)
{
if(what_ms<listLyricsTime[oo])
{
currentTime_out=oo+1;
break;
}
}
return true;
}
参数分别对应我们要解析的歌词文件的路径,还有就是我们要对比的时间点,
过程讲解:通过打开该路径的歌词文件,将保存歌词和时间的两个Qllist清空(listLyricsText,listLyricsTime),避免上一次解析后的数据干扰这个解析后的数据,读取每一行的数据保存到line,放到解析函数解析,整个文件解析完成之后就用一个for循环对比,找到比歌曲播放时间长的那个下标,就返回,用那个下标就可以找到我们想要的那句歌词。
歌单的显示以及歌单列表和歌曲关联起来
既然在一首歌和歌词之间都可以正常对应起来了,但是还有一个问题,就是,我们切换到另一首歌,但是歌词还是我们最开始解析的歌词,所以我们接下去要解决的问题就是随着歌曲的改变,歌词也要相对应。
歌单的显示,
这里为了大家看着不那么烦,就去掉了大部分注释,
给大家简单讲解一下子,就是我们打开一个文件夹,代码可知是一个music的文件夹,然后定义一个过滤器,过滤掉除了.wav格式之外的文件,把剩余的.wav文件保存到QFileInfoList里边,这里的info是前面我自己定义的一个结构体,里面的元素包括文件的名称和文件的目录。接下去就是各种转码操作,这里不是关键,关键的是if里边的几行代码。没一次将歌曲导入到mediaPlaylist里边的同时,都会把歌词名导入到listwidget里边。这样子下标就对应上了。
比如我们点击listwidget的第五行,它是listwidget的第五个元素,它的当前行是四,那么我们去查找mediaPlaylist的第四个下标(从0开始),就可以找到和歌词对应的歌曲。知道这个之后,后面就好操作了。
void Widget::scanning_file()
{
QDir dir("..//music");
QDir dirbsolutePath(dir.absolutePath());
/* 如果目录存在 */
if (dirbsolutePath.exists()) {
QStringList filter/* 定义过滤器 */
filter << "*.wav";
QFileInfoList files = dirbsolutePath.entryInfoList(filter, QDir::Files);/* 获取该目录下的所有文件 */
for (int i = 0; i < files.count(); i++) {
MediaObjectInfo info;
/* 使用 utf-8 编码 */
QString fileName = QString::fromUtf8(files.at(i).fileName() .replace(".wav", "") .toUtf8() .data());
info.fileName = fileName+".\n";
info.filePath = QString::fromUtf8(files.at(i) .filePath() .toUtf8() .data());
/* 媒体列表添加歌曲 */
if (mediaPlaylist->addMedia( QUrl::fromLocalFile(info.filePath))) {
mediaObjectInfo.append(info);/* 添加到容器数组里储存 */
ui->listWidget->addItem(info.fileName); /* 添加歌曲名字至列表 */
music_file_num++;//获取添加到列表里边的文件总数
}
}
}
}
一些细节部分的修修补补
1.要特别注意逻辑问题,比如说我们歌曲正常播放,播放完之后它就自己进入下一首,一切正常;但是如果还没播放完,我通过listwidget跳 转到别的歌曲,他还是会跳转到下一首,这样子导致的情况就是——歌曲没播完,我点击下一首,它给我播放下下首。
2.防止溢出问题,比如我播放到了最后一首,你还按下下一首的按钮,这让它上哪找下一首啊,找不到它就报溢出,我们就要做处理,重新指向第一首;第一首和上上首也是同样的道理
3.歌词显示时,我们要让它从最下面一直往上推,还是歌词第一句从中间向上下扩散,都要防止QList的溢出。
废话
下面给大家提供了例程代码,用的都是相对路径,可以直接使用,想要额外添加歌曲的,要把歌曲和歌词文件放在music文件夹下面,歌曲文件和歌词文件要同名哦,欢迎大家下载。下面给大家提供几个宝藏网站,我们用到的按钮的图片要是.icon格式的,可以从下面免费下载;歌词文件也是需要我们自己去下载的,我们平时下载的歌曲都是不包含.lvr文件的;由于我后期要移植到imx6ull上边,板子目前还没有把mplayer弄好,就只至此播放.wav格式的,我们可以通过下面的网站获取无损音乐,通过酷狗转成wav格式的(记住mp3是有损的,不支持转),或者在那个网站里直接下载wav格式的,就是歌曲比较少。
一些宝藏网站
icon图标下载
lvr歌词下载
无损音乐下载
源码下载,无需任何修改
恭喜这位帅哥喜提一朵小红花
恭喜这位靓仔喜提一朵小红花