使用Qt的工具完成一个音频播放器——FMusicPlayer


一、前言

利用Qt的工具开发一个音乐播放器,界面如下图:

界面截图

Qt版本5.12.12。该项目十分简单,适合用于练手。

二、模块构思

根据市场上现有的音乐播放器思考,一个基本的音乐播放器应该具有以下功能:

  • 播放/暂停

  • 停止

  • 控制音频播放进度

  • 管理音乐列表

  • 音量控制

  • 信息显示

  • ……

此软件仅实现了上述功能。

三、模块实现

此音乐播放器主要使用Qt现成的QMediaPlayer类和QMediaPlaylist类进行维护。

3.1 界面设计

整体界面设计如图:

界面设计

  • 9个按钮QPushButton,对应各种功能;

  • 3个标签文字QLabel,用于说明;

  • 2个文本框QLineEdit,用于显示信息;

  • 1个列表QListWidget,用于存放播放列表;

  • 2个滑条QSlider,一个用于控制和显示进度,一个用于控制和显示音量。

  • 若干个弹簧用于调整布局。

完成后的程序界面:

期望的UI

3.2 初始化工程

此程序使用了Qt的multimedia部件,所以首先在Qt的.pro工程文件中包含multimedia。

1
2
3
……
QT += multimedia
……

接着项目中应该有以下文件:

mainwindow.h
mainwindow.cpp
mainwindow.ui
main.cpp

  • 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);

// 添加文件少于1个时退出函数
if(filter.count() < 1) return;
// 将读取的文件放入musicplayList里
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
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类取出QListWidget的某一行
QListWidgetItem *item = ui->playList->currentItem();
if(item)
{
// 更新UI的音频名字
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刷新时间
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刷新时间
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(标准版)