基于Qt的高性能网络音乐播放器的设计与实现

2017-04-14 05:13刘永红赵卫东
关键词:播放器绘图鼠标

鄢 涛, 刘永红, 赵卫东, 余 悦, 曾 谊, 于 曦

(1.成都大学 模式识别与智能信息处理四川省高校重点实验室, 四川 成都 610106; 2.成都大学 信息科学与工程学院, 四川 成都 610106)

基于Qt的高性能网络音乐播放器的设计与实现

鄢 涛1,2, 刘永红1,2, 赵卫东1,2, 余 悦2, 曾 谊2, 于 曦1,2

(1.成都大学 模式识别与智能信息处理四川省高校重点实验室, 四川 成都 610106; 2.成都大学 信息科学与工程学院, 四川 成都 610106)

目前基于网络的音乐播放器功能普遍存在2个主要问题:广告太多;在后台运行不必要的进程而导致性能较低.针对这些情况,设计并实现了一款基于Qt的高性能网络音乐播放器.该播放器利用开放的音乐搜索应用程序编程接口,实现了在线搜索、在线播放、下载音乐及桌面歌词等功能.此外,用户界面设计中采用双缓冲绘图技术,并且程序经过大量代码层面的优化,使得该播放器纯净并拥有非常良好的性能表现.

Qt;在线音乐;播放器;高性能;双缓冲

0 引 言

互联网上有非常丰富的音乐资源,也有不少基于这些资源的音乐播放器.这些音乐播放器普遍功能都较强大,但出于商业因素,通常会嵌入很多广告、新闻,甚至会经常出现弹窗给用户带来影响.同时,由于软件功能的多样化和复杂性,这些音乐播放器很难做到性能优异[1-2].Qt是一个基于C++的跨平台图形用户界面(Graphical User Interface,GUI)应用程序开发框架,它提供给应用程序开发者建立艺术级GUI所需的所有功能,允许组件编程,且易扩展.此外,Qt提供了较丰富的套接字、传输控制协议、文件传输协议等与平台无关的类,能够方便地进行网络功能开发[3-7].本研究讨论了利用Qt的GUI框架和网络功能,以及双缓冲技术,实现了一款纯净、高性能的音乐播放器,其既具有网络音乐播放器的常用功能,也可以实现播放本地音乐.

1 核心功能设计

1.1 网络功能应用程序编程接口(Application Programming Interface,API)的封装

要实现在线试听,需要有相关的音乐搜索API.目前,许多播放器公司都提供开放的音乐搜索API,如酷狗音乐的API为:http://mobilecdn.kugou.com/api/v3/search/song?format=jsonp&keyword={0}&page={1}&pagesize={2}″&showtype=1&callback=kgJSONP238513750.其中,{0}表示需要搜索的歌曲或歌手,{1}表示查询的页码数,{2}表示当前页的返回数量.

以上API的请求方式为GET,返回数据为一个JSON对象,通过对JSON进行解析,就可以得到想要的数据.Qt已经提供了JSON解析的相关类,只需要根据酷狗音乐的JSON数据规则编写相关解析代码即可,

QVector analysis(QNetworkReply *reply)

{

QByteArray json=reply->readAll();/*API返回的是

JSON数据*/

QJsonParseError error;

QJsonDocument doucoument=QJsonDocument::fromJson(json,

&error);

QJsonObject obj=doucoument.object();

QJsonArray jsArray=obj.take(″data″).toObject().take

(″info″).toArray();/*有效数据*/

int size=jsArray.size();

QVector t(size);

for (int i=0;i

{

t[i].hashCode=jsArray[i].toObject().take(″hash″).

toString();/*音乐哈希码,通过它来实现播放*/

t[i].musicName=jsArray[i].toObject().take

(″songname″).toString();/*音乐名*/

t[i].singer=jsArray[i].toObject().take

(″singername″).toString();/*歌手名*/

t[i].duration=jsArray[i].toObject().take

(″duration″).toInt();/*音乐时长*/

}

return t;

}

1.2 定时器与界面更新

音乐播放的过程中,需要随时对主界面进行更新,更新的内容包括进度条及歌词等.这时,就需要使用操作系统提供的“定时器”功能,每隔一段时间对界面进行一次更新.Qt已经把“定时器”封装成一个叫做QTimer的类,这个类的使用也是非常简单的,只需要编写好处理函数即可.处理函数如下,

void update()

{

auto intToString=[this](size-t)->QString/*匿名函数,

用于把时间转换成一定格式的字符串*/

{

size-t min,sec;

QString minStr,secStr;

min=t/60;

t-=min*60;

sec=t;

minStr+=QString::number(min);

if (min<10)

{

minStr.push-front(′0′);

}

secStr+=QString::number(sec);

if (sec<10)

{

secStr.push-front(′0′);

}

return minStr+′:′+secStr;

};

playProgress=player->position();/*同步已播放的长度*/

lyricsBar->updateLyrics(playProgress);/*重绘桌面歌词*/

if (playProgress-lastUpdateTime>1000)/*进度条不必随

时更新,1 000 ms更新一次即可*/

{

lastUpdateTime=playProgress;

progress->setText(intToString(playProgress/1000)+

′/′+intToString(maxDuration/1000));

songSlider->setValue(playProgress/1000);

}

}

2 界面设计

2.1 无边框窗口拖动

每个GUI程序都可以通过标题栏来实现窗口拖动.为了音乐播放器的美观,通常会省略标题栏.如果想要任意拖动窗口,就需要编写相关事件代码来实现.

Windows系统中,存在各种“消息”,如鼠标消息、按键消息等.Qt已经把想要的“消息”封装成3个函数:mousePressEvent、mouseMoveEvent、mouseReleaseEvent.通过重写这些函数,即可实现对鼠标行为的定制,

void mouseReleaseEvent(QMouseEvent*event)

{

isPress=false;

}

void mousePressEvent(QMouseEvent*event)

{

lastPos=event->globalPos();/*记录鼠标的当前位置*/

isPress=true;/*标记鼠标是否在主面板上按下*/

}

void mouseMoveEvent(QMouseEvent*event)

{

if (isPress)/*鼠标按下的时候才移动,防止从子控件移

动到主面板上时产生的“瞬移”*/

{

int dx=event->globalX()-lastPos.x();

int dy=event->globalY()-lastPos.y();

lastPos=event->globalPos();

move(x()+dx,y()+dy);/*通过鼠标上次出现的位

置与当前位置的差,求出窗口的移动方向和长度*/

}

}其中,isPress是播放器的一个内部变量,它的存在十分关键.Qt提供了很多种无边框窗口移动的代码,但其几乎都没有isPress的存在.这种情况下,如果在播放器主面板的一个子控件上按下鼠标,然后把鼠标移动到主面板上,播放器窗口就会出现“瞬移”现象,因此必须引入isPress才能解决问题.

2.2 用QSS美化界面

QSS(Qt Style Sheets)是一种类似于WEB设计中层叠样式表(Cascading Style Sheets,CSS)技术的设计语言,它的目标和CSS相同,即实现表现与内容分离.通过QSS,可以很方便地实现界面的美化,而不需要编写大量用于控件自绘的代码.比如,设置按钮的背景图片,

QPushButton#closeBt

{

border-image:url(″data/icon/closeNormal.png″);

height:18px;

width:23px;

border:0px;

}

QPushButton#closeBt:hover

{

border-image:url(″data/icon/closeHover.png″);

height:18px;

width:23px;

border:0px;

}

QPushButton#closeBt:pressed

{

border-image:url(″data/icon/closePress.png″);

height:18px;

width:23px;

border:0px;

}

同时,QSS还能对按钮closeBt的普通、悬停、按下3个状态分别设置不同背景图片(见图1).

图1 QSS美化之后的播放器界面

2.3 皮肤更换

QSS能完成绝大多数界面美化工作,但不包括播放器主面板这个顶级窗口的美化.一方面,让用户通过修改QSS的方式来更换皮肤是非常不人性化的;另一方面,QSS也难以应对用户选择图片当皮肤的需求.所以,要实现皮肤更换,只有重写paintEvent实现窗口自绘.主要功能代码如下,

void paintEvent(QPaintEvent*e)

{

QPainter painter(this);

painter.drawPixmap(rect(),skin);/*按照窗口的大小绘制

图片*/

QWidget::paintEvent(e);/*调用父类的绘图函数,保证其

他部分能正确绘制*/

}

其中,skin是一个QPixmap类的对象,里面容纳着当前的皮肤.更换皮肤时,只需要修改skin,然后重绘界面即可.

3 歌词的处理

3.1 用正则表达式解析歌词

歌词的处理看似简单,实际上比较复杂.目前,一种主流的歌词文件格式是.Lrc,要解析的正是这种格式的歌词文件.Lrc文件的每一行格式如下,

[mm:ss.xx]歌词内容

其中,mm表示分钟数,ss表示秒数,xx表示百分之一秒数.这个数据指明某一句歌词出现的具体时间,而中括号后面的内容则是歌词的正式内容.

Lrc文件的规律性如此之强,以至于可以直接使用正则表达式来对其进行解析.很多厂商都提供了正则表达式解析引擎,C++ 11标准也让正则表达式解析进入了C++的标准.不过由于播放器基于Qt开发,所以最终采用Qt提供的解析器.正则表达式的解析代码如下,

QRegExp regexp;

regexp.setPattern(″\d{2}(?=:)″);/*设置匹配模式,匹配 一个长度为2的数字及:*/

regexp.indexIn(lyrics);

int minute=regexp.cap(0).toInt();/*将匹配到的第一个作为 分钟数*/

regexp.setPattern(″\d{2}(?=\.)″);/*匹配一个长度为 2的数字及.*/

regexp.indexIn(lyrics);

int second=regexp.cap(0).toInt();/*将匹配到的第一个作为 秒数*/

regexp.setPattern(″\d{2}(?=\])″);/*匹配一个长度为 2的数字及]*/

regexp.indexIn(lyrics);

int millisecond = regexp.cap(0).toInt();/*将匹配到的第一个 作为百分之一秒数*/

int duration=minute*60000+second*1000+millisecond*10; /*简单的时间转换*/

regexp.setPattern(″\[\d{2}:\d{2}\.\d{2}\]″);

QString lrcString=lyrics.replace(regexp,″″);/*将所有的时间戳

替换成空字符串,结果即为歌词*/

以上代码只说明了解析的过程,实际代码要更复杂一些.

3.2 滚动歌词的实现

大多数播放器显示歌词时,都会给歌词添加一点“动态”效果,看起来就像在“滚动”一样.实际上,所谓的“滚动”效果,实现起来并不复杂.绘制歌词时,绘制两层文字:第一层完全绘制,第二层则根据进度来绘制其中某些部分.由于界面更新速度非常快,所以看起来就好像是歌词在滚动一般.相关实现为,

void paint()

{

QPainter painter(this);

painter.setFont(font);

painter.setPen(QColor(0,0,0));

painter.drawText(1,1,800,58,Qt::AlignVCenter|Qt::

AlignLeft,first);/*绘制一层黑色文字作为

基底,让歌词显得更有质感*/

painter.setPen(QPen(normalGradient1,0));/*绘制第一层

渐变文字,渐变可以被用户所设置*/

painter.drawText(0,0,800,58,Qt::AlignVCenter|Qt::

AlignLeft,first);/*对齐方式为左对齐*/

painter.setPen(QPen(maskGradient1,0));/*绘制遮罩层*/

painter.drawText(0,0,progress*maxPix,58,Qt::

AlignVCenter|Qt::AlignLeft,first);/*progress是当前进度,

通过这个参数可以控制遮罩部分的宽度*/

}

3.3 双缓冲绘图技术

播放器需要绘制的歌词非常多,除了桌面歌词,还有嵌在主面板的窗口歌词,而这些歌词需要经常更新(设定50 ms更新一次).在Debug模式下,音乐播放器平均CPU占用为5%(CPU主频为2.6 GHz).如果换用Release模式,理论上能把CPU占用减少到3%,但播放器的性能依然太差.

通过对代码分析发现,形如painter.drawText这样的代码非常多,性能瓶颈也正是来自这样的代码.从内存(显存)向屏幕绘制图像,速度非常慢.如果每50 ms都要进行大量绘制工作,CPU的开销就相当大.这时,可采用双缓冲绘图技术,即先一次性把所有文字绘制到内存(显存)中,然后再一次性把内存中的数据绘制到屏幕上.由于往内存绘制文字的速度远远高于往屏幕绘制的速度,所以即使双缓冲绘图看起来多了一些额外工作,而实际上却拥有更好的性能表现.双缓冲的原理如下,

void paint()

{

QPixmap pix(width,height);

pix.fill(Qt::transparent);/*新建画布,并用透明色填充*/

QPainter painter(&pix);/*现在painter将在画布上绘图*/

painter.setFont(font);

painter.setPen(QColor(255,255,255));

painter.drawText(firstRect, Qt::AlignCenter, firstText);

…/*大量的文字绘制*/

painter.setPen(QColor(255,255,255));

painter.drawText(seventhRect,Qt::AlignCenter,seventhText);

painter.drawPixmap(rect(),pix);/*将画布中的内容一次

性绘制到屏幕上*/

}

实际上,以上代码也只展示了双缓冲绘图的基本操作方式,实际代码根据需要做了优化,比如,不必每次都绘制底层文字,也不必每次都重新填充画布,完全可以在绘制完所有底层文字之后,将画布保存起来,以后每次更新时,只需往画布上绘制遮罩层即可,直到歌词内容需要改变为止(见图2).

图2 最终效果

采用双缓冲绘图技术优化之后,播放器在Debug下的平均CPU占用减少到2.5%,在Release模式下更是减少到了1%.这样的性能表现非常优秀,因为主流播放器的平均CPU占用都在2%到3%之间.

4 结 论

本研究设计并实现了一款基于Qt技术的网络音乐播放器.该播放器纯净无广告、功能完善且性能高.此外,开发过程中所用到的无边框窗口拖动技术,也可广泛应用于桌面开发中.需要指出的是,本研究提出的解决方案虽然不复杂,但却比大多数方案更有效,其中,歌词解析时灵活使用了正则表达式,而双缓冲绘图技术的应用,更是大大提高了绘图效率.这些技术的总结,对与Qt相关的多媒体开发具有重要参考价值.

[1]Blanchette J,Summerfield M.C++ GUI Qt 4编程[M].闫锋欣,曾泉人,张志强,译.北京:电子工业出版社,2013.

[2]焦正才,樊文侠.基于Qt/Embedded的MP3音乐播放器的设计与实现[J].电子设计工程,2012,20(7):148-150.

[3]Gregoire M,Solter N A,Klerper S J.C++高级编程[M].侯普秀,郑思遥,译.北京:清华大学出版社,2014.

[4]Wong Michael,IBM XL编译器中国开发团队.深入理解C++ 11[M].北京:机械工业出版社,2016.

[5]蔡志明.精通Qt 4编程[M].北京:电子工业出版社,2011.

[6]Summerfield M.Qt高级编程[M].白建平,王军锋,闫锋欣,译.北京:电子工业出版社,2011.

[7]刘晓立,赵俊逸.基于Qt的音乐播放器[J].软件导刊,2015,14(10):112-114.

Design and Implementation of High Performance Online Music Player Based on Qt

YANTao1,2,LIUYonghong1,2,ZHAOWeidong1,2,YUYue2,ZENGYi2,YUXi1,2

(1.Key Laboratory of Pattern Recognition and Intelligent Information Processing of Sichuan Province, Chengdu University, Chengdu 610106, China; 2.School of Information Science and Engineering, Chengdu University, Chengdu 610106, China)

Most music players based on Internet have their own powerful functions presently.However,there are two main problems in these music players:one is that there are too many advertisements and the other is that their performance is low due to some processes running background.In order to solve these problems,this paper designs and implements a high performance music player based on Qt software.By fully utilizing the open music search API,the player implements some functions such as online search,online playing,downloading music,desktop lyrics,etc.Furthermore,by using double-buffering technology UI design,and through the optimization of the coding,the player proposed now by this paper is pure and of high performance.

Qt;online music;player;high performance;double-buffering

1004-5422(2017)01-0055-05

2016-11-03.

四川省科技厅软科学研究计划(2017ZR0198)资助项目.

鄢 涛(1973 — ), 男, 硕士, 副教授, 从事计算机软件工程研究.

TN912.23+1;TP311.52

A

猜你喜欢
播放器绘图鼠标
来自河流的你
“禾下乘凉图”绘图人
Progress in Neural NLP: Modeling, Learning, and Reasoning
Walkman诞生40周年 索尼适时发布NW-ZX500和NW-A100系列播放器
基于STM32的MP3播放器设计
垂涎三尺
Moon ACE播放器/放大器一体机
播放器背板注塑模具设计
45岁的鼠标
超能力鼠标