qtmplayer/mainwindow.cpp

416 lines
15 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <pthread.h>
int mplayer_pid; // 子进程号
pthread_t t1,t2; // 线程
char current_song_path_name[128] = ""; // 当前歌曲路径
int currentVolume = 20; // 当前音量 10
void *getTimerThreadFunc(void *arg); // 发送获取当前时间的 mplayer 命令
void *readPipeMSG(void *arg); // 获取当前时间的线程
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
QSize windowSize = QSize(geometry().width(), geometry().height());
// 设置背景图片
setAutoFillBackground(true);
QPalette pal = this->palette();
pal.setBrush(backgroundRole(),QPixmap(":/img/background1").scaled(windowSize, Qt::KeepAspectRatio));
setPalette(pal);
initMainWindow(); // 初始化主界面
mplayerInit(); // 初始化 mplayer
kill(mplayer_pid,SIGSTOP);
scanSong(); // 扫描歌曲列表
setSongList(); // 将扫描的歌曲显示到歌曲列表中
// 捕获当前时间信号
connect(this, SIGNAL(currentTimeSignal(int)), this, SLOT(setCurrentTimeLabel(int)));
// 建立歌曲进度滑块修改信号通讯
connect(this, SIGNAL(currentProgressSignal(int)), this, SLOT(setCurrentSlider(int)));
// 建立音量大小滑块信号通讯
// connect(this, SIGNAL(currentVolumeSignal(int)), this, SLOT(setVolumeSlider(int)));
}
MainWindow::~MainWindow()
{
if(mplayer_pid != 0)
kill(mplayer_pid,SIGKILL);
delete ui;
}
void MainWindow::initMainWindow()
{
// 设置主窗体属性
setWindowTitle("音乐播放器");
setFixedSize(1055,750);
setWindowIcon(QIcon(":/icon/button"));
// 设置子组件背景透明
ui->song_list_widget->setAttribute(Qt::WA_TranslucentBackground);
ui->label_artist->setAttribute(Qt::WA_TranslucentBackground);
ui->label_song_list->setAttribute(Qt::WA_TranslucentBackground);
ui->label_song_name->setAttribute(Qt::WA_TranslucentBackground);
ui->label_meta_album->setAttribute(Qt::WA_TranslucentBackground);
ui->contrl_widget->setAttribute(Qt::WA_TranslucentBackground);
ui->widget_show->setAttribute(Qt::WA_TranslucentBackground);
ui->volume_widget->setAttribute(Qt::WA_TranslucentBackground);
ui->widget_3->setAttribute(Qt::WA_TranslucentBackground);
ui->time_left_label->setAttribute(Qt::WA_TranslucentBackground);
ui->time_right_label->setAttribute(Qt::WA_TranslucentBackground);
ui->time_widget->setAttribute(Qt::WA_TranslucentBackground);
// 设置按钮属性
// 设置滑块属性
ui->time_slider->setStyleSheet("QSlider::groove:horizontal { background-color: gray; }"
"QSlider::handle:horizontal { background-color: yellow; width: 20px;}");
ui->volume_slider->setStyleSheet("QSlider::groove:horizontal { background-color: gray; }"
"QSlider::handle:horizontal { background-color: yellow; width: 20px;}");
ui->volume_slider->setMinimum(0); // 最小音量 0
ui->volume_slider->setMaximum(100); // 最大音量 100
// 当前音量从 mplayer 属性获取
// QSlider* slider = new QSlider(Qt::Horizontal);
// slider->setMinimum(0); // 设置最小值
// slider->setMaximum(100); // 设置最大值
// slider->setValue(50); // 设置当前值
// slider->setSingleStep(1); // 设置步长
}
void MainWindow::mplayerInit()
{
// 创建一个无名管道
pipe(fd);
// 创建有名管道
mkfifo("fifo_cmd", 0666);
// 创建 子进程启动 mplayer
pid_t pid = fork();
if(pid == 0) // 子进程
{
// 重定向标准输出
dup2(fd[1], 1);
// 使用 exec 启动 mplayer
// execlp("mplayer","mplayer","-idle","-slave","-quiet","/home/flykhan/qtmplayer/song/StopLove.mp3",NULL);
execlp("/usr/bin/mplayer","mplayer","-idle","-slave","-quiet","-input","file=fifo_cmd","/home/flykhan/qtmplayer/song/Diamonds.mp3",NULL);
_exit(-1); // 异常退出
}
else if(pid>0) // 父进程
{
mplayer_pid = pid;
pthread_create(&t1,NULL,getTimerThreadFunc,this);
pthread_create(&t2,NULL,readPipeMSG,this);
volume_control(currentVolume, 1); // 初始音量设置为 currentVolume->50
setVolumeSlider(currentVolume); // 设置音量滑块初始位置
// pthread_detach(t1);
// pthread_detach(t2);
// 打开管道
// 以写的方式打开(阻塞到某进程 以读的方式打开)
fifo_fd = open("fifo_cmd", O_WRONLY);
if(fifo_fd < 0){
perror("open");
// return 0;
_exit(0); // 管道打开失败直接返回
}
}
}
void MainWindow::scanSong()
{
// 1. 得到目录句柄
DIR *dir = opendir("/home/flykhan/qtmplayer/song");
if(NULL == dir)
{
perror("opendir");
_exit(0); // 目录为空返回
}
// 2. 根据目录句柄扫描当前文件
while(1)
{
// 读取文件目录
struct dirent *dirent = readdir(dir);
if(NULL==dirent)
break;
// 判断类型为 DT_REG->文件类型
if(dirent->d_type == DT_REG)
{
// 如果扫描到的文件名包含 ".mp3",则将该文件名添加到向量数组中
if(strstr(dirent->d_name, ".mp3") != NULL)
{
vectorSong.push_back(string(dirent->d_name));
}
}
}
// 关闭目录句柄
closedir(dir);
}
void MainWindow::playNextSong()
{
index++;
if(index == vectorSong.size())
index = 0; // 当索引到达歌曲最后一个时,将索引置为 0 ,循环索引
char changeSongBuf[128] = "";
// 将 loadfile 'song_name_path' 写入字符数组
int len = sprintf(changeSongBuf, "loadfile /home/flykhan/qtmplayer/song/%s\n", vectorSong[index].c_str());
// int len = sprintf(changeSongBuf, "loadfile ./song/%s\n", vectorSong[index].c_str());
qDebug() << changeSongBuf << endl; // 控制台打印下一首歌的名字
// write(this->fifo_fd, changeSongBuf.toUtf8().toStdString().c_str(),changeSongBuf.toUtf8().toStdString().size());
ui->song_list_widget->setCurrentRow(index);
write(this->fifo_fd, changeSongBuf, len); // 将获取的下一首歌曲名写入到有名管道中
}
void MainWindow::playLastSong()
{
index--;
if(index == -1)
index = vectorSong.size() - 1; // 当索引到达歌曲 0 时,将索引置为最后一个歌曲,循环索引
char changeSongBuf[128] = "";
// 将 loadfile 'song_name_path' 写入字符数组
int len = sprintf(changeSongBuf, "loadfile /home/flykhan/qtmplayer/song/%s\n", vectorSong[index].c_str());
// int len = sprintf(changeSongBuf, "loadfile ./song/%s\n", vectorSong[index].c_str());
qDebug() << changeSongBuf << endl; // 控制台打印下一首歌的名字
// write(this->fifo_fd, changeSongBuf.toUtf8().toStdString().c_str(),changeSongBuf.toUtf8().toStdString().size());
ui->song_list_widget->setCurrentRow(index);
write(this->fifo_fd, changeSongBuf, len); // 将获取的下一首歌曲名写入到有名管道中
}
void MainWindow::setCurrentTimeLabel(int time)
{
int m = 0, s = 0;
m = time / 60; // 算分钟
s = time % 60; // 算秒钟
char buf[32] = "";
sprintf(buf,"%02d:%02d",m,s);
// 将歌曲当前时间设置到 label
ui->time_left_label->setText(QString(buf));
}
void MainWindow::setCurrentSlider(int currentProgress)
{
// 设置时间滑动条状态
ui->time_slider->setValue(currentProgress);
}
void MainWindow::setVolumeSlider(int currentVolume)
{
// 设置音量滑动条状态
ui->volume_slider->setValue(currentVolume);
}
void *getTimerThreadFunc(void *arg)
{
MainWindow *p = (MainWindow *)arg;
while (1)
{
// 发送获取的当前时间的命令
// 发送 get_time_pos 命令到管道中
write(p->fifo_fd, "get_time_pos\n", strlen("get_time_pos\n"));
usleep(100*1000); // 休眠 0.5 秒
// 发送获取当前歌曲进度的命令
write(p->fifo_fd,"get_percent_pos\n",strlen("get_percent_pos\n"));
usleep(100*1000); // 休眠 0.5 秒
// // 发送获取的当前歌曲元数据信息的命令
// // 发送获取当前歌曲长度的命令 ANS_LENGTH
// write(p->fifo_fd, "get_time_length\n", strlen("get_time_length\n"));
// usleep(125*1000);
// // 发送获取当前歌曲名的命令 ANS_META_TITLE
// write(p->fifo_fd, "get_file_name\n", strlen("get_file_name\n"));
// usleep(125*1000);
// // 发送获取当前歌曲歌手的命令 ANS_META_ARTIST
// write(p->fifo_fd, "get_meta_artist\n", strlen("get_meta_artist\n"));
// usleep(125*1000);
// // 发送获取当前歌曲专辑的命令 ANS_META_ALBU
// write(p->fifo_fd, "get_meta_album\n", strlen("get_meta_album\n"));
// usleep(125*1000);
}
}
void *readPipeMSG(void *arg)
{
MainWindow *p = (MainWindow *)arg;
// 不停的获取无名管道的数据
while (1)
{
char buf[256] = "";
int ret = read(p->fd[0], buf, sizeof(buf)); // 从无名管道读取内容到 buf
if(ret > 0)
{
// 判断消息的类型 :
// 如果消息是 "当前歌曲播放时间位置"
if(strncmp(buf,"ANS_TIME_POSITION",strlen("ANS_TIME_POSITION")) == 0)
{
int time = 0;
sscanf(buf,"ANS_TIME_POSITION=%d",&time); // 从数据流 buf 中读取时间到 time
// 给 UI 发送当前时间信号
emit p->currentTimeSignal(time);
}
// 如果消息是 "当前歌曲播放进度"
else if(strncmp(buf,"ANS_PERCENT_POSITION", strlen("ANS_PERCENT_POSITION")) == 0)
{
int currentProgress = 0;
sscanf(buf, "ANS_PERCENT_POSITION=%d", &currentProgress);
// 给 UI 发送进度信号
emit p->currentProgressSignal(currentProgress);
}
// // 如果消息是 "当前歌曲元数据信息"
// else if(strncmp(buf,"ANS_LENGTH", strlen("ANS_LENGTH")) == 0)
// {
// int currentProgress = 0;
// sscanf(buf, "ANS_LENGTH=%d", &currentProgress);
// // 给 UI 发送进度信号
// emit p->currentProgressSignal(currentProgress);
// }
}
}
}
void MainWindow::setSongList()
{
// 将 vector 内容转存入 QStringList
QStringList strList;
for(vector<string>::const_iterator it = vectorSong.begin(); it != vectorSong.end(); ++it)
{
strList.append(QString::fromStdString(*it));
}
ui->song_list_widget->addItems(strList);
// ui->song_list_widget->setCurrentRow(2);
}
void MainWindow::on_play_btn_clicked()
{
if(play_btn_flag == 0)
{
ui->play_btn->setIcon(QIcon(":/icon2/pause"));
kill(mplayer_pid,SIGCONT);
play_btn_flag = 1;
}
else if (play_btn_flag == 1)
{
ui->play_btn->setIcon(QIcon(":/icon2/play"));
kill(mplayer_pid,SIGSTOP);
play_btn_flag = 0;
}
}
void MainWindow::on_back_btn_clicked()
{
playLastSong();
}
void MainWindow::on_front_btn_clicked()
{
playNextSong(); // 切换下一首歌
}
void MainWindow::player_rewind_or_forward(int seconds)
{
char changeSongBuf[128] = "";
int len = sprintf(changeSongBuf, "seek %d\n", seconds); // 快进快退 seconds 秒 (seconds 为正->进,为负->退)
write(this->fifo_fd, changeSongBuf, len); // 将快进快退指令写入到有名管道中
}
void MainWindow::volume_control(int value, int abs)
{
char changeSongBuf[128] = "";
// 音量调整到 value 大小(-数减+数加) abs 为 0 时在当前音量上加减一个值abs 不为 0 时将当前音量设置为 value 大小
int len = sprintf(changeSongBuf, "volume %d %d\n", value, abs);
write(this->fifo_fd, changeSongBuf, len); // 将音量控制指令写入到有名管道中
ui->volume_label->setText(QString::number(currentVolume) + " %");
}
void MainWindow::volume_mute_switch(int mute_arg)
{
char changeSongBuf[128] = "";
int len = sprintf(changeSongBuf, "mute %d\n", mute_arg); // mute_arg 静音开关参数 (1->静音0->取消静音)
write(this->fifo_fd, changeSongBuf, len); // 将静音控制指令写入到有名管道中
}
void MainWindow::on_last_btn_clicked()
{
player_rewind_or_forward(-10); // 快退 10 秒
}
void MainWindow::on_next_btn_clicked()
{
player_rewind_or_forward(10); // 快退 10 秒
}
void MainWindow::on_mute_btn_clicked()
{
if(mute_flag == 0) // 没静音则按下后设置静音
{
ui->mute_btn->setIcon(QIcon(":/icon2/sound-on"));
// volume_mute_switch(1);
// mute_flag = 1;
volume_mute_switch(++mute_flag);
ui->volume_down_btn->setDisabled(true);
ui->volume_up_btn->setDisabled(true);
ui->volume_slider->setDisabled(true);
ui->volume_slider->setStyleSheet("QSlider::groove:horizontal { background-color: white; }"
"QSlider::handle:horizontal { background-color: gray; width: 20px;}");
ui->volume_label->setText("Muted");
}
else if (mute_flag == 1) // 已静音则按下后取消静音
{
ui->mute_btn->setIcon(QIcon(":/icon2/sound-off"));
volume_mute_switch(--mute_flag);
ui->volume_down_btn->setDisabled(false);
ui->volume_up_btn->setDisabled(false);
ui->volume_slider->setDisabled(false);
ui->volume_slider->setStyleSheet("QSlider::groove:horizontal { background-color: gray; }"
"QSlider::handle:horizontal { background-color: yellow; width: 20px;}");
ui->volume_label->setText(QString::number(currentVolume) + " %");
}
}
void MainWindow::on_volume_down_btn_clicked()
{
if(currentVolume-2 >= 0)
{
currentVolume -= 2;
volume_control(currentVolume, 1); // 减 10 音量
setVolumeSlider(currentVolume); // 设置音量滑块位置
}
}
void MainWindow::on_volume_up_btn_clicked()
{
if(currentVolume+2 <= 100)
{
currentVolume += 2;
volume_control(currentVolume, 1); // 加 10 音量
// emit this->currentVolumeSignal(currentVolume);
setVolumeSlider(currentVolume); // 设置音量滑块位置
}
}
void MainWindow::on_volume_slider_sliderMoved(int position)
{
currentVolume = position;
volume_control(position, 1);
}