鄢 濤, 劉永紅, 趙衛(wèi)東, 余 悅, 曾 誼, 于 曦
(1.成都大學(xué) 模式識(shí)別與智能信息處理四川省高校重點(diǎn)實(shí)驗(yàn)室, 四川 成都 610106; 2.成都大學(xué) 信息科學(xué)與工程學(xué)院, 四川 成都 610106)
基于Qt的高性能網(wǎng)絡(luò)音樂(lè)播放器的設(shè)計(jì)與實(shí)現(xiàn)
鄢 濤1,2, 劉永紅1,2, 趙衛(wèi)東1,2, 余 悅2, 曾 誼2, 于 曦1,2
(1.成都大學(xué) 模式識(shí)別與智能信息處理四川省高校重點(diǎn)實(shí)驗(yàn)室, 四川 成都 610106; 2.成都大學(xué) 信息科學(xué)與工程學(xué)院, 四川 成都 610106)
目前基于網(wǎng)絡(luò)的音樂(lè)播放器功能普遍存在2個(gè)主要問(wèn)題:廣告太多;在后臺(tái)運(yùn)行不必要的進(jìn)程而導(dǎo)致性能較低.針對(duì)這些情況,設(shè)計(jì)并實(shí)現(xiàn)了一款基于Qt的高性能網(wǎng)絡(luò)音樂(lè)播放器.該播放器利用開放的音樂(lè)搜索應(yīng)用程序編程接口,實(shí)現(xiàn)了在線搜索、在線播放、下載音樂(lè)及桌面歌詞等功能.此外,用戶界面設(shè)計(jì)中采用雙緩沖繪圖技術(shù),并且程序經(jīng)過(guò)大量代碼層面的優(yōu)化,使得該播放器純凈并擁有非常良好的性能表現(xiàn).
Qt;在線音樂(lè);播放器;高性能;雙緩沖
互聯(lián)網(wǎng)上有非常豐富的音樂(lè)資源,也有不少基于這些資源的音樂(lè)播放器.這些音樂(lè)播放器普遍功能都較強(qiáng)大,但出于商業(yè)因素,通常會(huì)嵌入很多廣告、新聞,甚至?xí)?jīng)常出現(xiàn)彈窗給用戶帶來(lái)影響.同時(shí),由于軟件功能的多樣化和復(fù)雜性,這些音樂(lè)播放器很難做到性能優(yōu)異[1-2].Qt是一個(gè)基于C++的跨平臺(tái)圖形用戶界面(Graphical User Interface,GUI)應(yīng)用程序開發(fā)框架,它提供給應(yīng)用程序開發(fā)者建立藝術(shù)級(jí)GUI所需的所有功能,允許組件編程,且易擴(kuò)展.此外,Qt提供了較豐富的套接字、傳輸控制協(xié)議、文件傳輸協(xié)議等與平臺(tái)無(wú)關(guān)的類,能夠方便地進(jìn)行網(wǎng)絡(luò)功能開發(fā)[3-7].本研究討論了利用Qt的GUI框架和網(wǎng)絡(luò)功能,以及雙緩沖技術(shù),實(shí)現(xiàn)了一款純凈、高性能的音樂(lè)播放器,其既具有網(wǎng)絡(luò)音樂(lè)播放器的常用功能,也可以實(shí)現(xiàn)播放本地音樂(lè).
1.1 網(wǎng)絡(luò)功能應(yīng)用程序編程接口(Application Programming Interface,API)的封裝
要實(shí)現(xiàn)在線試聽,需要有相關(guān)的音樂(lè)搜索API.目前,許多播放器公司都提供開放的音樂(lè)搜索API,如酷狗音樂(lè)的API為:http://mobilecdn.kugou.com/api/v3/search/song?format=jsonp&keyword={0}&page={1}&pagesize={2}″&showtype=1&callback=kgJSONP238513750.其中,{0}表示需要搜索的歌曲或歌手,{1}表示查詢的頁(yè)碼數(shù),{2}表示當(dāng)前頁(yè)的返回?cái)?shù)量.
以上API的請(qǐng)求方式為GET,返回?cái)?shù)據(jù)為一個(gè)JSON對(duì)象,通過(guò)對(duì)JSON進(jìn)行解析,就可以得到想要的數(shù)據(jù).Qt已經(jīng)提供了JSON解析的相關(guān)類,只需要根據(jù)酷狗音樂(lè)的JSON數(shù)據(jù)規(guī)則編寫相關(guān)解析代碼即可,
QVector
{
QByteArray json=reply->readAll();/*API返回的是
JSON數(shù)據(jù)*/
QJsonParseError error;
QJsonDocument doucoument=QJsonDocument::fromJson(json,
&error);
QJsonObject obj=doucoument.object();
QJsonArray jsArray=obj.take(″data″).toObject().take
(″info″).toArray();/*有效數(shù)據(jù)*/
int size=jsArray.size();
QVector
for (int i=0;i { t[i].hashCode=jsArray[i].toObject().take(″hash″). toString();/*音樂(lè)哈希碼,通過(guò)它來(lái)實(shí)現(xiàn)播放*/ t[i].musicName=jsArray[i].toObject().take (″songname″).toString();/*音樂(lè)名*/ t[i].singer=jsArray[i].toObject().take (″singername″).toString();/*歌手名*/ t[i].duration=jsArray[i].toObject().take (″duration″).toInt();/*音樂(lè)時(shí)長(zhǎng)*/ } return t; } 1.2 定時(shí)器與界面更新 音樂(lè)播放的過(guò)程中,需要隨時(shí)對(duì)主界面進(jìn)行更新,更新的內(nèi)容包括進(jìn)度條及歌詞等.這時(shí),就需要使用操作系統(tǒng)提供的“定時(shí)器”功能,每隔一段時(shí)間對(duì)界面進(jìn)行一次更新.Qt已經(jīng)把“定時(shí)器”封裝成一個(gè)叫做QTimer的類,這個(gè)類的使用也是非常簡(jiǎn)單的,只需要編寫好處理函數(shù)即可.處理函數(shù)如下, void update() { auto intToString=[this](size-t)->QString/*匿名函數(shù), 用于把時(shí)間轉(zhuǎn)換成一定格式的字符串*/ { 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();/*同步已播放的長(zhǎng)度*/ lyricsBar->updateLyrics(playProgress);/*重繪桌面歌詞*/ if (playProgress-lastUpdateTime>1000)/*進(jìn)度條不必隨 時(shí)更新,1 000 ms更新一次即可*/ { lastUpdateTime=playProgress; progress->setText(intToString(playProgress/1000)+ ′/′+intToString(maxDuration/1000)); songSlider->setValue(playProgress/1000); } } 2.1 無(wú)邊框窗口拖動(dòng) 每個(gè)GUI程序都可以通過(guò)標(biāo)題欄來(lái)實(shí)現(xiàn)窗口拖動(dòng).為了音樂(lè)播放器的美觀,通常會(huì)省略標(biāo)題欄.如果想要任意拖動(dòng)窗口,就需要編寫相關(guān)事件代碼來(lái)實(shí)現(xiàn). Windows系統(tǒng)中,存在各種“消息”,如鼠標(biāo)消息、按鍵消息等.Qt已經(jīng)把想要的“消息”封裝成3個(gè)函數(shù):mousePressEvent、mouseMoveEvent、mouseReleaseEvent.通過(guò)重寫這些函數(shù),即可實(shí)現(xiàn)對(duì)鼠標(biāo)行為的定制, void mouseReleaseEvent(QMouseEvent*event) { isPress=false; } void mousePressEvent(QMouseEvent*event) { lastPos=event->globalPos();/*記錄鼠標(biāo)的當(dāng)前位置*/ isPress=true;/*標(biāo)記鼠標(biāo)是否在主面板上按下*/ } void mouseMoveEvent(QMouseEvent*event) { if (isPress)/*鼠標(biāo)按下的時(shí)候才移動(dòng),防止從子控件移 動(dòng)到主面板上時(shí)產(chǎn)生的“瞬移”*/ { int dx=event->globalX()-lastPos.x(); int dy=event->globalY()-lastPos.y(); lastPos=event->globalPos(); move(x()+dx,y()+dy);/*通過(guò)鼠標(biāo)上次出現(xiàn)的位 置與當(dāng)前位置的差,求出窗口的移動(dòng)方向和長(zhǎng)度*/ } }其中,isPress是播放器的一個(gè)內(nèi)部變量,它的存在十分關(guān)鍵.Qt提供了很多種無(wú)邊框窗口移動(dòng)的代碼,但其幾乎都沒(méi)有isPress的存在.這種情況下,如果在播放器主面板的一個(gè)子控件上按下鼠標(biāo),然后把鼠標(biāo)移動(dòng)到主面板上,播放器窗口就會(huì)出現(xiàn)“瞬移”現(xiàn)象,因此必須引入isPress才能解決問(wèn)題. 2.2 用QSS美化界面 QSS(Qt Style Sheets)是一種類似于WEB設(shè)計(jì)中層疊樣式表(Cascading Style Sheets,CSS)技術(shù)的設(shè)計(jì)語(yǔ)言,它的目標(biāo)和CSS相同,即實(shí)現(xiàn)表現(xiàn)與內(nèi)容分離.通過(guò)QSS,可以很方便地實(shí)現(xiàn)界面的美化,而不需要編寫大量用于控件自繪的代碼.比如,設(shè)置按鈕的背景圖片, 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; } 同時(shí),QSS還能對(duì)按鈕closeBt的普通、懸停、按下3個(gè)狀態(tài)分別設(shè)置不同背景圖片(見圖1). 圖1 QSS美化之后的播放器界面 2.3 皮膚更換 QSS能完成絕大多數(shù)界面美化工作,但不包括播放器主面板這個(gè)頂級(jí)窗口的美化.一方面,讓用戶通過(guò)修改QSS的方式來(lái)更換皮膚是非常不人性化的;另一方面,QSS也難以應(yīng)對(duì)用戶選擇圖片當(dāng)皮膚的需求.所以,要實(shí)現(xiàn)皮膚更換,只有重寫paintEvent實(shí)現(xiàn)窗口自繪.主要功能代碼如下, void paintEvent(QPaintEvent*e) { QPainter painter(this); painter.drawPixmap(rect(),skin);/*按照窗口的大小繪制 圖片*/ QWidget::paintEvent(e);/*調(diào)用父類的繪圖函數(shù),保證其 他部分能正確繪制*/ } 其中,skin是一個(gè)QPixmap類的對(duì)象,里面容納著當(dāng)前的皮膚.更換皮膚時(shí),只需要修改skin,然后重繪界面即可. 3.1 用正則表達(dá)式解析歌詞 歌詞的處理看似簡(jiǎn)單,實(shí)際上比較復(fù)雜.目前,一種主流的歌詞文件格式是.Lrc,要解析的正是這種格式的歌詞文件.Lrc文件的每一行格式如下, [mm:ss.xx]歌詞內(nèi)容 其中,mm表示分鐘數(shù),ss表示秒數(shù),xx表示百分之一秒數(shù).這個(gè)數(shù)據(jù)指明某一句歌詞出現(xiàn)的具體時(shí)間,而中括號(hào)后面的內(nèi)容則是歌詞的正式內(nèi)容. Lrc文件的規(guī)律性如此之強(qiáng),以至于可以直接使用正則表達(dá)式來(lái)對(duì)其進(jìn)行解析.很多廠商都提供了正則表達(dá)式解析引擎,C++ 11標(biāo)準(zhǔn)也讓正則表達(dá)式解析進(jìn)入了C++的標(biāo)準(zhǔn).不過(guò)由于播放器基于Qt開發(fā),所以最終采用Qt提供的解析器.正則表達(dá)式的解析代碼如下, QRegExp regexp; regexp.setPattern(″\d{2}(?=:)″);/*設(shè)置匹配模式,匹配 一個(gè)長(zhǎng)度為2的數(shù)字及:*/ regexp.indexIn(lyrics); int minute=regexp.cap(0).toInt();/*將匹配到的第一個(gè)作為 分鐘數(shù)*/ regexp.setPattern(″\d{2}(?=\.)″);/*匹配一個(gè)長(zhǎng)度為 2的數(shù)字及.*/ regexp.indexIn(lyrics); int second=regexp.cap(0).toInt();/*將匹配到的第一個(gè)作為 秒數(shù)*/ regexp.setPattern(″\d{2}(?=\])″);/*匹配一個(gè)長(zhǎng)度為 2的數(shù)字及]*/ regexp.indexIn(lyrics); int millisecond = regexp.cap(0).toInt();/*將匹配到的第一個(gè) 作為百分之一秒數(shù)*/ int duration=minute*60000+second*1000+millisecond*10; /*簡(jiǎn)單的時(shí)間轉(zhuǎn)換*/ regexp.setPattern(″\[\d{2}:\d{2}\.\d{2}\]″); QString lrcString=lyrics.replace(regexp,″″);/*將所有的時(shí)間戳 替換成空字符串,結(jié)果即為歌詞*/ 以上代碼只說(shuō)明了解析的過(guò)程,實(shí)際代碼要更復(fù)雜一些. 3.2 滾動(dòng)歌詞的實(shí)現(xiàn) 大多數(shù)播放器顯示歌詞時(shí),都會(huì)給歌詞添加一點(diǎn)“動(dòng)態(tài)”效果,看起來(lái)就像在“滾動(dòng)”一樣.實(shí)際上,所謂的“滾動(dòng)”效果,實(shí)現(xiàn)起來(lái)并不復(fù)雜.繪制歌詞時(shí),繪制兩層文字:第一層完全繪制,第二層則根據(jù)進(jìn)度來(lái)繪制其中某些部分.由于界面更新速度非常快,所以看起來(lái)就好像是歌詞在滾動(dòng)一般.相關(guān)實(shí)現(xiàn)為, 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);/*繪制一層黑色文字作為 基底,讓歌詞顯得更有質(zhì)感*/ painter.setPen(QPen(normalGradient1,0));/*繪制第一層 漸變文字,漸變可以被用戶所設(shè)置*/ painter.drawText(0,0,800,58,Qt::AlignVCenter|Qt:: AlignLeft,first);/*對(duì)齊方式為左對(duì)齊*/ painter.setPen(QPen(maskGradient1,0));/*繪制遮罩層*/ painter.drawText(0,0,progress*maxPix,58,Qt:: AlignVCenter|Qt::AlignLeft,first);/*progress是當(dāng)前進(jìn)度, 通過(guò)這個(gè)參數(shù)可以控制遮罩部分的寬度*/ } 3.3 雙緩沖繪圖技術(shù) 播放器需要繪制的歌詞非常多,除了桌面歌詞,還有嵌在主面板的窗口歌詞,而這些歌詞需要經(jīng)常更新(設(shè)定50 ms更新一次).在Debug模式下,音樂(lè)播放器平均CPU占用為5%(CPU主頻為2.6 GHz).如果換用Release模式,理論上能把CPU占用減少到3%,但播放器的性能依然太差. 通過(guò)對(duì)代碼分析發(fā)現(xiàn),形如painter.drawText這樣的代碼非常多,性能瓶頸也正是來(lái)自這樣的代碼.從內(nèi)存(顯存)向屏幕繪制圖像,速度非常慢.如果每50 ms都要進(jìn)行大量繪制工作,CPU的開銷就相當(dāng)大.這時(shí),可采用雙緩沖繪圖技術(shù),即先一次性把所有文字繪制到內(nèi)存(顯存)中,然后再一次性把內(nèi)存中的數(shù)據(jù)繪制到屏幕上.由于往內(nèi)存繪制文字的速度遠(yuǎn)遠(yuǎn)高于往屏幕繪制的速度,所以即使雙緩沖繪圖看起來(lái)多了一些額外工作,而實(shí)際上卻擁有更好的性能表現(xiàn).雙緩沖的原理如下, void paint() { QPixmap pix(width,height); pix.fill(Qt::transparent);/*新建畫布,并用透明色填充*/ QPainter painter(&pix);/*現(xiàn)在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);/*將畫布中的內(nèi)容一次 性繪制到屏幕上*/ } 實(shí)際上,以上代碼也只展示了雙緩沖繪圖的基本操作方式,實(shí)際代碼根據(jù)需要做了優(yōu)化,比如,不必每次都繪制底層文字,也不必每次都重新填充畫布,完全可以在繪制完所有底層文字之后,將畫布保存起來(lái),以后每次更新時(shí),只需往畫布上繪制遮罩層即可,直到歌詞內(nèi)容需要改變?yōu)橹?見圖2). 圖2 最終效果 采用雙緩沖繪圖技術(shù)優(yōu)化之后,播放器在Debug下的平均CPU占用減少到2.5%,在Release模式下更是減少到了1%.這樣的性能表現(xiàn)非常優(yōu)秀,因?yàn)橹髁鞑シ牌鞯钠骄鵆PU占用都在2%到3%之間. 本研究設(shè)計(jì)并實(shí)現(xiàn)了一款基于Qt技術(shù)的網(wǎng)絡(luò)音樂(lè)播放器.該播放器純凈無(wú)廣告、功能完善且性能高.此外,開發(fā)過(guò)程中所用到的無(wú)邊框窗口拖動(dòng)技術(shù),也可廣泛應(yīng)用于桌面開發(fā)中.需要指出的是,本研究提出的解決方案雖然不復(fù)雜,但卻比大多數(shù)方案更有效,其中,歌詞解析時(shí)靈活使用了正則表達(dá)式,而雙緩沖繪圖技術(shù)的應(yīng)用,更是大大提高了繪圖效率.這些技術(shù)的總結(jié),對(duì)與Qt相關(guān)的多媒體開發(fā)具有重要參考價(jià)值. [1]Blanchette J,Summerfield M.C++ GUI Qt 4編程[M].閆鋒欣,曾泉人,張志強(qiáng),譯.北京:電子工業(yè)出版社,2013. [2]焦正才,樊文俠.基于Qt/Embedded的MP3音樂(lè)播放器的設(shè)計(jì)與實(shí)現(xiàn)[J].電子設(shè)計(jì)工程,2012,20(7):148-150. [3]Gregoire M,Solter N A,Klerper S J.C++高級(jí)編程[M].侯普秀,鄭思遙,譯.北京:清華大學(xué)出版社,2014. [4]Wong Michael,IBM XL編譯器中國(guó)開發(fā)團(tuán)隊(duì).深入理解C++ 11[M].北京:機(jī)械工業(yè)出版社,2016. [5]蔡志明.精通Qt 4編程[M].北京:電子工業(yè)出版社,2011. [6]Summerfield M.Qt高級(jí)編程[M].白建平,王軍鋒,閆鋒欣,譯.北京:電子工業(yè)出版社,2011. [7]劉曉立,趙俊逸.基于Qt的音樂(lè)播放器[J].軟件導(dǎo)刊,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. 四川省科技廳軟科學(xué)研究計(jì)劃(2017ZR0198)資助項(xiàng)目. 鄢 濤(1973 — ), 男, 碩士, 副教授, 從事計(jì)算機(jī)軟件工程研究. TN912.23+1;TP311.52 A2 界面設(shè)計(jì)
3 歌詞的處理
4 結(jié) 論