使用Qt的工具完成一个音频播放器——FMusicPlayer
关键词:Qt、C++
一、前言
利用Qt的工具开发一个音乐播放器,界面如下图:
Qt版本5.12.12。该项目十分简单,适合用于练手。
二、模块构思
根据市场上现有的音乐播放器思考,一个基本的音乐播放器应该具有以下功能:
-
播放/暂停
-
停止
-
控制音频播放进度
-
管理音乐列表
-
音量控制
-
信息显示
-
……
此软件仅实现了上述功能。
三、模块实现
此音乐播放器主要使用Qt现成的QMediaPlayer类和QMediaPlaylist类进行维护。
3.1 界面设计
整体界面设计如图:
完成后的程序界面:
3.1 初始化工程
此程序使用了Qt的multimedia部件,所以首先在Qt的.pro工程文件中包含multimedia。
接着项目中应该有以下文件:
mainwindow.h
mainwindow.cpp
mainwindow.ui
main.cpp
首先在mainwindow.h中将我们实现的模块添加槽函数声明和成员变量的声明:
1 2 3 4 5 6
| QMediaPlayer *musicplayer; QMediaPlaylist *musicplayList;
QString durationTime; QString positionTime;
|
接着在主窗口构造函数中做一些初始化:
1 2 3 4 5 6 7 8 9 10 11
| musicplayer = new QMediaPlayer(this); musicplayList = new QMediaPlaylist(this);
musicplayList->setPlaybackMode(QMediaPlaylist::Loop); musicplayer->setPlaylist(musicplayList);
ui->volume->setValue(50); changedVolume(50);
|
3.3 播放/暂停模块
当点击播放键时播放音乐,为播放按钮play绑定单击事件,触发playMusic()函数:
1 2
| connect(ui->play, SIGNAL(clicked()), this, SLOT(playMusic()));
|
1 2 3 4 5 6 7
| void MainWindow::playMusic() { if(musicplayList->currentIndex()<0) musicplayList->setCurrentIndex(0); musicplayer->play(); }
|
当点击暂停键时暂停音乐,支持记录位置,从上次暂停的位置播放。为暂停按钮绑定单击事件,触发pauseMusic()函数:
1 2
| connect(ui->pause, SIGNAL(clicked()), this, SLOT(pauseMusic()));
|
1 2 3 4
| void MainWindow::pauseMusic() { musicplayer->pause(); }
|
3.4 停止模块
当点击停止键时,停止音乐,音乐播放位置归零。为停止按钮绑定单击事件,触发stopMusic()函数:
1 2
| connect(ui->stop, SIGNAL(clicked()), this, SLOT(stopMusic()));
|
1 2 3 4
| void MainWindow::stopMusic() { musicplayer->stop(); }
|
3.5 控制音频进度模块
播放时,音频进度条应当可以被拖动,并且更新播放的位置。为进度条绑定值修改事件,触发changedPosition()函数,函数参数为进度条传入的位置参数。
1 2
| connect(ui->timebar, SIGNAL(valueChanged(int)), this, SLOT(changedPosition(int)));
|
1 2 3 4
| void MainWindow::changedPosition(int pos) { musicplayer->setPosition(pos); }
|
3.6 管理音乐列表模块
播放器应该可以输入多个文件,接着顺序播放,所以需要维护一个音乐列表。
首先可以向音乐列表添加音乐,为添加音乐按钮绑定单击事件,绑定addMusic()函数。
1 2
| connect(ui->addMusic, SIGNAL(clicked()), this, SLOT(addMusictoList()));
|
读取文件时,wav文件是支持的,而mp3文件需要外置解码器的支持,处理mp3文件方法见文章最后。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| void MainWindow::addMusictoList() { QString curPath = QDir::currentPath(); QString title = "选择音频文件"; QString filter = "音频文件(*.wav *.mp3)"; QStringList fileList = QFileDialog::getOpenFileNames(this, title, curPath, filter);
if(filter.count() < 1) return; for(int i = 0; i < fileList.count(); ++ i) { QString aFile = fileList.at(i); musicplayList->addMedia(QUrl::fromLocalFile(aFile));
QFileInfo fileinfo(aFile); ui->playList->addItem(fileinfo.fileName()); } if(musicplayer->state() != QMediaPlayer::PlayingState) musicplayList->setCurrentIndex(0); }
|
同样还应支持移除音频文件:
1 2
| connect(ui->delMusic, SIGNAL(clicked()), this, SLOT(delMusic()));
|
移除文件时还应判断移除的是否为当前播放音乐,进行相应处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| void MainWindow::delMusic() { int pos = ui->playList->currentRow(); QListWidgetItem *item = ui->playList->takeItem(pos); delete item;
if(musicplayList->currentIndex() == pos) { int nextPos = 0; if(pos >= 1) nextPos = pos - 1; musicplayList->removeMedia(pos); if(ui->playList->count() > 0) { musicplayList->setCurrentIndex(nextPos); onPlayListChanged(nextPos); } else { musicplayer->stop(); ui->playmusic->setText("无文件"); } } else musicplayList->removeMedia(pos); }
|
清空文件则是移除文件的简化版,直接将播放列表清空即可。
1 2
| connect(ui->clearList, SIGNAL(clicked()), this, SLOT(clearList()));
|
1 2 3 4 5 6 7 8 9
| void MainWindow::clearList() { musicplayList->clear(); ui->playList->clear(); ui->playmusic->clear(); ui->time->clear(); ui->timebar->setValue(0); musicplayer->stop(); }
|
最后还可以添加上一首和下一首的功能,只需简单绑定一下点击事件和调用musicplayList的函数即可。
1 2
| musicplayList->previous(); musicplayList->next();
|
3.7 音量控制模块
一个播放器还应该支持音量的调节,而QMediaPlayer支持音量调节,所以绑定滑条值改变事件且调用函数即可,事件返回一个滑条的位置参数。
1 2
| connect(ui->volume, SIGNAL(valueChanged(int)), this, SLOT(changedVolume(int)));
|
1 2 3 4
| void MainWindow::changedVolume(int value) { musicplayer->setVolume(value); }
|
3.8 信息显示与同步模块
根据UI的设计,显示的信息有:当前播放的音频名字、播放时间/完整时间。当播放音频进行切换时,需要更新音频名字、播放时间和音频完整时间等信息。
为播放列表musicplayList的当前索引变化事件绑定onPlayListChanged()函数,事件返回新的索引号,用此更新播放的音频名字。
1 2
| connect(musicplayList, SIGNAL(currentIndexChanged(int)), this, SLOT(onPlayListChanged(int)));
|
1 2 3 4 5 6 7 8 9 10 11
| void MainWindow::onPlayListChanged(int pos) { ui->playList->setCurrentRow(pos); QListWidgetItem *item = ui->playList->currentItem(); if(item) { ui->playmusic->setText(item->text()); } }
|
切换音频时,其总时长也应该变化,需要为播放器musicplayer的播放位置变化durationChanged事件绑定onDurationChanged()函数。函数为计算音频总的时间。
1 2
| connect(musicplayer, SIGNAL(durationChanged(qint64)), this, SLOT(onDurationChanged(qint64)));
|
1 2 3 4 5 6 7 8 9 10 11
| void MainWindow::onDurationChanged(qint64 dur) { ui->timebar->setMaximum(dur);
int secs = int(dur / 1000); int mins = secs / 60; secs %= 60; durationTime = QString::asprintf(" %d : %d ", mins, secs); ui->time->setText(positionTime + " / " + durationTime); }
|
至于音频随着播放而位置和进度的刷新,则需要为播放器musicplayer的播放位置变化positionChanged事件绑定onPositionChanged()函数。函数为计算播放的时间。
1 2
| connect(musicplayer, SIGNAL(positionChanged(qint64)), this, SLOT(onPositionChanged(qint64)));
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| void MainWindow::onPositionChanged(qint64 pos) { if(ui->timebar->isSliderDown()) return;
ui->timebar->setSliderPosition(pos); int secs = int (pos / 1000); int mins = secs / 60; secs %= 60; positionTime = QString::asprintf(" %d : %d ", mins, secs); ui->time->setText(positionTime + " / " + durationTime); }
|
四、总结
该程序源代码放在Gitee - https://gitee.com/fingsinz/fmusicplayer/
该项目只是一个简单的播放器实现,希望能够帮助萌新认识Qt,提高自己的能力。
不支持直接加载mp3文件,需要先下载相关解码器。此处附上解码器链接:
K-lite_codec_pack
K-lite_codec_pack_basic(基础版)
K-lite_codec_pack_standard(标准版)