最近有一个课程实验,利用window上的API接口实现MP3播放器。 突发奇想,在linux下用相关接口做一个MP3播放器。最先的打算是完全使用纯C写的,但后来感觉编写调试实在费劲。于是就使用了QT这个IDE工具,还可以编写个图形界面的。这篇博客主要贴代码,理论部分,请参见我的另一篇博客(基于QT实现Mp3播放器(理论部分))
来,小二,上个图(注重逻辑,界面并没有认真规划,⁄(⁄ ⁄•⁄ω⁄•⁄ ⁄)⁄)。
playerprocess.cpp
#include "playerprocess.h"
#include
PlayerProcess::PlayerProcess(QObject *parent) : QObject(parent)
{
process = NULL;
}
//播放Mp3文件
void PlayerProcess::play(QString mp3_file)
{
if(mp3_file.isEmpty() == false)
{
QString play_tool = "mplayer"; //利用mplayer工具进行播放
QStringList argus;
process = new QProcess(); //每次新生成一个播放进程
//以下是播放时的参数
argus<<"-slave";
argus<<"-quiet";
argus<<mp3_file;
//开始播放
process->start(play_tool,argus);
if(process->waitForStarted(5000) == false) //等待播放进程开始
qDebug()<<"fail to pause or replay the player";
}
else
qDebug()<<"file name is empty!!!";
}
//暂停
void PlayerProcess::Pause()
{
process->write("pause\n"); //利用mplayer提供的借口,向进程写入参数
}
//重新开始
void PlayerProcess::Rebegin()
{
process->write("pause\n"); //利用mplayer提供的借口,向进程写入参数
}
//加音量:
void PlayerProcess::Up(int volume)
{
QString cmd = "volume " + QString::number(volume)+" 2 \n"; //利用mplayer提供的借口,向进程写入参数
process->write(cmd.toUtf8());
}
//减音量
void PlayerProcess::Down(int volume)
{
QString cmd = "volume " + QString::number(volume)+" 2 \n"; //利用mplayer提供的借口,向进程写入参数
process->write(cmd.toUtf8());
}
//结束当前播放进程
void PlayerProcess::End()
{
if(process != NULL) //判断是否是自动播放完毕停止,还是手动更换曲目停止
{
QStringList args;
process->kill(); //kill 当前进程
if(process->waitForFinished(5000) == false) //等待进程结束
{
qDebug()<<"fail to kill the last song";
}
else
{
delete process;
qDebug()<<"kill the last song";
}
}
}
//获取播放进程
QProcess* PlayerProcess::GetProcess()
{
return process;
}
//设置播放进程
void PlayerProcess::SetProcess(QProcess * process)
{
this->process = process;
}
player.cpp
#include "player.h"
#include
Player::Player(QObject *parent) : QObject(parent)
{
volume = 50; //初始化音量为50.音量范围为0-100
status = Player::InitStatus ; //初始化状态
cur_song = -2; //init status
}
PlayerProcess* Player::GetPlaPro()
{
return &playerprocess;
}
//返回当前播放器状态
Player::PlayStatus Player::GetPlaySta()
{
return status;
}
//判断一个状态是否正常定义(仅限枚举变量底层用连续int实现)
bool Player::IsIncInPlaySta(PlayStatus sta)
{
if(sta >= Player::InitStatus && sta<= Player::PauseStatus )
return true;
else
return false;
}
//设置播放器状态
void Player::SetPlaySta(PlayStatus sta)
{
if(IsIncInPlaySta(sta))
status = sta;
else
qDebug()<<"player status is invalid";
}
//设置当前歌曲
void Player::SetCurSong(int index)
{
if(index >= -1 && index <= song_list.size()) //-1代表下一次播放从最后一首开始。song_lise.size()代表下一次播放从头开始
cur_song = index;
else
qDebug()<<"player index is invalid";
}
//获得播放器歌曲
int Player::GetCurSong()
{
return cur_song;
}
//设置歌曲列表
void Player::SetSongList(QStringList list)
{
if(list.empty() == false)
song_list = list;
}
//添加歌曲到列表
void Player::AddSongToList(QStringList add_songs)
{
if(add_songs.empty() == false)
song_list.append(add_songs);
}
//获取歌曲列表
QStringList Player::GetSongList()
{
return song_list;
}
//删除歌曲
void Player::DelSong(int index)
{
if(index >= 0 && index <= song_list.size()-1)
{
song_list.removeAt(cur_song);
}
}
//删除当前歌曲
void Player::DelCurSong()
{
DelSong(cur_song);
}
//加音量
void Player::Up()
{
if(volume < 100)
{
volume+=10;
playerprocess.Up(volume);
}
}
//减音量
void Player::Down()
{
if(volume > 0)
{
volume-=10;
playerprocess.Down(volume);
}
}
//重新开始一个播放
void Player::NewPlay()
{
if(song_list.size() == 0) //若歌曲列表为空
{
qDebug()<<"song list is empty";
return ;
}
else
{
if(cur_song == -1) //若是第一首(0开始)以前的歌曲,就循环到歌曲列表的最后一首
cur_song = song_list.size()-1;
else if(cur_song == -2 || cur_song == song_list.size()) //若是超过最后一首,则回到第一首(0开始)
cur_song = 0;
}
QString play_song = song_list.at(cur_song); //获得歌曲列表内容
playerprocess.play(play_song); //播放
}
//播放歌曲
void Player::Play()
{
if(status == Player::InitStatus) //初始状态
{
NewPlay(); //一个新的播放任务开始
status = Player::RunningStatus; //设置为运行状态
}
else if(status == Player::RunningStatus) //若是运行状态
{
playerprocess.Pause(); //暂停
status = Player::PauseStatus; //设置为运行状态
}
else
{
playerprocess.Rebegin(); //重新开始
status = Player::RunningStatus; //设置为运行状态
}
}
//下一曲
void Player::Next()
{
if(cur_song == -2) //init status
cur_song = 0;
else
++cur_song;
}
// 上一曲
void Player::Pre()
{
if(cur_song == -2) //init status
cur_song = 0;
else
--cur_song;
}
//kill当前播放进程
void Player::End()
{
playerprocess.End();
}
widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include
#include
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
InitWindow(); //初始化窗口
}
Widget::~Widget()
{
delete ui;
}
//初始化窗口:大小、颜色、背景等
void Widget::InitWindow()
{
setWindowTitle("EasyMp3Player");
setFixedSize(this->width(),this->height());
//设置背景
QPalette PAllbackground = this->palette();
QImage ImgAllbackground(":/back.png");
QImage fitimgpic=ImgAllbackground.scaled(this->width(),this->height(), Qt::IgnoreAspectRatio);
PAllbackground.setBrush(QPalette::Window, QBrush(fitimgpic));
this->setPalette(PAllbackground);
QFont item_font;
item_font.setPixelSize(50);
item_font.setBold(true);
ui->listWidget->setStyleSheet("background-color:transparent; QListWidget::item(color:black)");
}
//高亮选中的歌曲栏目
void Widget::HighLigCurSong()
{
int cur_song = player.GetCurSong() ;
QStringList song_list = player.GetSongList();
if(cur_song>=0 && cur_song <= song_list.size()-1)
{
QListWidgetItem * tmp = ui->listWidget->item(cur_song);
tmp->setBackgroundColor(QColor("orange")); //当前播放歌曲高亮
}
}
//显示歌曲列表
void Widget::ShowSongList()
{
ui->listWidget->clear();
QStringList songs;
QStringList song_list = player.GetSongList();
foreach (QString song_dir, song_list)
{
songs.append(QFileInfo(song_dir).fileName());
}
ui->listWidget->addItems(songs);
HighLigCurSong(); //高亮
}
//点击播放按钮
void Widget::on_pushButton_play_clicked()
{
Player::PlayStatus status = player.GetPlaySta();
player.Play(); //播放歌曲
if(status == Player::InitStatus)
ConnectNewProcess(); //连接新的player进程到自动播放结束槽
//关联每个播放进程结束信号和槽
ShowSongList(); //在QListWidget中显示歌曲列表
}
//下一曲
void Widget::on_pushButton_next_clicked()
{
Player::PlayStatus status = player.GetPlaySta();
if(status == Player::RunningStatus)
player.End();
player.Next(); //下一曲
player.SetPlaySta(Player::InitStatus); //回到初始状态
on_pushButton_play_clicked();
}
//上一曲
void Widget::on_pushButton_pre_clicked()
{
Player::PlayStatus status = player.GetPlaySta();
if(status == Player::RunningStatus) //是否是从运行状态点击上一曲
player.End();
player.SetPlaySta(Player::InitStatus); //回到初始状态
player.Pre(); //上一曲
on_pushButton_play_clicked();
}
//添加歌曲到播放列表
void Widget::on_pushButton_Add_clicked()
{
QStringList file_names = QFileDialog::getOpenFileNames(this,tr("choose a song"),".","music(*.mp3)");
player.AddSongToList(file_names);
ShowSongList(); //show song list
}
//删除当前歌曲
void Widget::on_pushButton_del_clicked()
{
player.DelCurSong(); //删除当前歌曲
ShowSongList();
player.End(); //结束当前播放歌曲
player.SetPlaySta(Player::InitStatus);
on_pushButton_play_clicked();
}
//加音量
void Widget::on_pushButton_up_clicked()
{
player.Up();
}
//减音量
void Widget::on_pushButton_down_clicked()
{
player.Down();
}
//连接新的player进程到自动播放结束槽
bool Widget::ConnectNewProcess()
{
QProcess * tmp = player.GetPlaPro()->GetProcess();
if(tmp != NULL)
{
connect(tmp ,SIGNAL(finished(int,QProcess::ExitStatus)),this,SLOT(PlayEnd(int,QProcess::ExitStatus)));
return true;
}
else
{
qDebug()<<"connect new player process failed";
return false;
}
}
//一首歌曲自动播放结束
void Widget::PlayEnd(int code, QProcess::ExitStatus exit_status)
{
if(exit_status == QProcess::NormalExit) //正常退出(注意:切换下一曲、上一曲为播放进程为非正常退出)
{
player.GetPlaPro()->SetProcess(NULL); //设置为空
on_pushButton_next_clicked(); //下一曲
ShowSongList(); //显示列表
}
}
//程序退出,把所有的播放进程删除
void Widget::closeEvent(QCloseEvent *event)
{
player.End();
}
//退出
void Widget::on_pushButton_Quit_clicked()
{
close();
}
具体的代码都比较简单,注释也相对详细。这里只说一个点(不关心的可以略过)。如下
当前歌曲播放完毕之后需要切换下一首,因为是每次使用QProcess新进程播放音乐,所以主进程和播放的进程相对独立,这样的话就必须在新建QProcess进程时connect播放进程的退出状态到具体的槽(在这个槽中进行新建下一个播放进程,否则达不到自动切换的目的,同时也为了更改(高亮)下一首歌曲)。
问题是如果使用上述分层的设计按理说,这部分connect的代码应该放在player这层中,但是player是做为一个widget的一个对象,这样的话,就没法调用包括ShowSongList()的槽。 这个问题想了很久,尝试了很多方法,还是不太满意。最后只能把connect连接放在widget中实现了。
具体的工程代码已经打包了,有需要的请到对应的资源页进行下载。