一、基础原理
1、多进程系统编程
通过execlp(“mplayer”,“mplayer”,“-slave”,“-quiet”,“-idle”,“-input”,“file=./myfifo”,“-playlist”,“./list.txt”,NULL);
将造作可以通过有名管道./myfifo传给mplayer。
2、C语言对于文本的操作
对歌词文本进行解析。
二、myplayer的工作模式
1)键盘模式:
按下一个按键,程序开始工作
启动方法:
mplayer 歌曲路径名
启动mplayer后可以通过键盘按键来控制mplayer播放
基本控制键:
left or right 向后/向前搜索10秒
up or down 向后/向前搜索1分钟
pageup or pagedown 向后/向前搜索10分钟
p or SPACE 暂停播放(按任意键继续)
q or ESC 停止播放并退出
0 or 9 音量控制(音量循环模式)
2)slave模式:(-------用于项目)
启动方法:
./mplayer -idle -slave -quiet 歌曲路径名
-ac mad 这两个参数的意思是指定用mad解码器
-idle 播放文件至文件末尾后mplayer不退出
-slave 指定mplayer运行在slave模式下
-quiet 指定mplayer不向屏幕上,打印乱码信息
slave模式下的控制命令:
loadfile string //播放string指定的歌曲。
loadlist listname //播放列表文件
volume x 1 //设置音量,x为音量的大小。
mute 1/0 //静音开关。1:静音;0:取消静音。
pause //暂停/取消暂停。
seek value //快进或快退参数value指定的秒数
当value为正时,快进;当value为负时,快退。
get_percent_pos //获得文件的播放进度(百分比:0–100)。
get_time_pos //获得文件的当前位置,以秒为单位,精确到小数位1位。
get_file_name //获得文件的文件名。
get_time_length //获得文件的长度,以秒为单位。
get_meta_album //获得文件的 ‘专辑’ 的元数据。
get_meta_artist //获得文件的 ‘艺术家’ 的元数据。
get_meta_comment //获得文件的 ‘评论’ 的元数据。
get_meta_genre //获得文件的 ‘流派’ 的元数据。
get_meta_title //获得文件的 ‘标题’ 的元数据。
get_meta_year //获得文件的 ‘年份’ 的元数据。
三、对歌词的解析操作
在Qt中我们可以针对歌词创建一个类
lyc.h
#ifndef LYC_H
#define LYC_H
#include <iostream>
# include <vector>
#include <string.h>
#include <stdlib.h>
#include <QObject>
class LrcContent{
public:
int lrc_time;
char lrc_content[256];
};
class Lyc
{
public:
Lyc(char * filename);
~Lyc();
void getSongMsg();
void getSongLrc();
public:
//歌词文件名
char lyc_name[256];
//歌曲名
char song_name[256];
//歌曲作者
char song_atuther[256];
//专辑名
char song_album[256];
//所有的歌词内容
std::vector <LrcContent *> lrcVector;
//单句歌词的操作句柄
LrcContent * lrc;
//歌词文件指针
FILE * fd;
int lrc_time;
};
#endif // LYC_H
lyc.c
#include "lyc.h"
#include <QDebug>
char tempbuff[256]={};
Lyc::Lyc(char * filename)
{
//根据歌曲名找到歌词名
char * temp = strtok(filename,".");//因为在解析歌曲的同时就顺便将歌词的也解析了,所以传来的的是.mp3结尾的字符串
sprintf(lyc_name,"%s.lrc",temp);//字符串的拼接
//打开歌词文件
fd = fopen(lyc_name,"r+");
//未发现歌词文件
if(fd==NULL)
{
perror("fopen:");
//歌曲名
//char song_name[256];
memset(song_name,0,sizeof(song_name));
strcpy(song_name,"无");
//歌曲作者
//har song_atuther[256];
memset(song_atuther,0,sizeof(song_atuther));
strcpy(song_atuther,"无");
//专辑名
//char song_album[256];
memset(song_album,0,sizeof(song_album));
strcpy(song_album,"无");
}
else
{
//获取歌曲信息
getSongMsg();
//获取歌曲信息
getSongLrc();
}
}
Lyc::~Lyc()
{
fclose(fd);
}
//解析歌曲信息
char * DealMsgString(char *buff)
{
memset(tempbuff,0,sizeof(tempbuff));
buff+=4;
int i=0;
while(*buff !=']')
{
tempbuff[i]=*buff;
i++;
buff++;
}
return tempbuff;
}
void Lyc::getSongMsg()
{
char buff[256]={};
for(int i=0;i<3;i++)
{
//如果前三行没有内容
if(fgets(buff,256,fd)==NULL)
{
//歌曲名
//char song_name[256];
memset(song_name,0,sizeof(song_name));
strcpy(song_name,"无");
//歌曲作者
//char song_atuther[256];
memset(song_atuther,0,sizeof(song_atuther));
strcpy(song_atuther,"无");
//专辑名
//char song_album[256];
memset(song_album,0,sizeof(song_album));
strcpy(song_album,"无");
}
if(strncmp(buff,"[ti:",4)==0)
{
char *temp= DealMsgString(buff);
memset(song_name,0,sizeof(song_name));
strcpy(song_name,temp);
}
else if(strncmp(buff,"[ar:",4)==0)
{
char *temp= DealMsgString(buff);
memset(song_atuther,0,sizeof(song_atuther));
strcpy(song_atuther,temp);
}
else if(strncmp(buff,"[al:",4)==0)
{
char *temp= DealMsgString(buff);
memset(song_album,0,sizeof(song_album));
strcpy(song_album,temp);
}
else
{
//歌曲名
memset(song_name,0,sizeof(song_name));
strcpy(song_name,"无");
//歌曲作者
memset(song_atuther,0,sizeof(song_atuther));
strcpy(song_atuther,"无");
//专辑名
memset(song_album,0,sizeof(song_album));
strcpy(song_album,"无");
}
}
}
void Lyc::getSongLrc()
{
this->lrcVector.clear();;
char text[256]={};
int min=0;
int sec=0;
while(fgets(text,256,fd) != NULL)
{
char * temp = text;
int lrctime;
//非空行
if(text[0] == '[')
{
if(text[1]>='0'||text[1]<='9')
{
//解析时间
sscanf(text,"[%d:%d",&min,&sec);
lrctime = min*60+sec;
temp+=10;
}
//判断是否为停顿
if(*temp=='\0'||*temp=='\n')
{
continue;
}
lrc = new LrcContent;
memset(lrc->lrc_content,0,sizeof(lrc->lrc_content));
strcpy(lrc->lrc_content,temp);
lrc->lrc_time=lrctime;
lrcVector.push_back(lrc);
}
}
}
四、对mplayer的命令操作
//创建歌单
void createPlaylist(const char* music_folder,const char* playlist_file)
{
//read directory(get song name)
DIR* dir;
struct dirent* entry;
FILE* file;
dir=opendir(music_folder);
if(dir == NULL)
{
printf("Open dir tmpdir fail\n");
return;
}
file = fopen(playlist_file,"w");
if(file==NULL)
{
printf("无法创建歌单文件\n");
closedir(dir);
return;
}
while ((entry = readdir(dir)) != NULL) {
// 如果是以.mp3或.wav结尾的文件,则将其写入歌单文件
if (strcasecmp(entry->d_name + strlen(entry->d_name) - 4, ".mp3") == 0
|| strcasecmp(entry->d_name + strlen(entry->d_name) - 4, ".wav") == 0) {
fprintf(file, "%s/%s\n", music_folder, entry->d_name);
strcpy(song_lry[j++],entry->d_name);
qDebug()<<entry->d_name;
}
}
fclose(file);
closedir(dir);
printf("歌单创建成功!\n");
return ;
}
createPlaylist(“歌曲所在地址”,“list.txt”);//创建歌单
1、创建父子进程,和有名管道。子进程控制mplayer,父进程维持ui界面,并通过有名管道给传命令。
//判断有名管道是否打开
if(fd < 0)//全局变量(已经定义好的有名管道)
{
perror("open:");
}
//create pipe
pipe(pipe_fd);
//create parent child process
pid = vfork();
if(pid == 0)//子进程
{
dup2(pipe_fd[1],1);//输出重定向,可以读到输出的内容
execlp("mplayer","mplayer","-slave","-quiet","-idle","-input","file=./myfifo","-playlist","./list.txt",NULL);
}
else if(pid > 0)
{
sleep(1);
char buff1[20];
char buff[1024];//清除管道中的数据
read(pipe_fd[0],buff,sizeof(buff));//清除管道中的数据
ui->horizontalSlider_2->setMaximum(lyc->lrcVector.back()->lrc_time);//设置歌曲进度条的最大值
ui->horizontalSlider_2->setMinimum(0);//设置歌曲进度条的最小值
//歌曲进度条的时间
int min = 0;
int sec = 0;
min = song_time/60;
sec = song_time%60;
char mytime[6]={};
sprintf(mytime,"%02d:%02d",min,sec);
this->ui->label_3->setText(mytime);//进度条右边的歌曲时间
int timelast = lyc->lrcVector.back()->lrc_time;
min = timelast/60;
sec = timelast%60;
mytime[6]={};
sprintf(mytime,"%02d:%02d",min,sec);
this->ui->label_4->setText(mytime);//进度条左边的歌曲总时间
//获取歌曲音量
write(fd,"get_property volume\n",strlen("get_property volume\n"));//向有名管道写时间
memset(buff1,0,sizeof(buff1));
read(pipe_fd[0],buff1,sizeof(buff1));
int value;
sscanf(buff1,"ANS_volume=%d",&value);
ui->horizontalSlider->setMaximum(100);
ui->horizontalSlider->setMinimum(0);
ui->horizontalSlider->setValue(value);
volume=value;
//时间函数,让歌曲时间每秒加一
timer = new QTimer(this);
connect(timer,SIGNAL(timeout()),this,SLOT(on_horizontalSlider_2_add()));
//connect(timer,SIGNAL(timeout()),this,SLOT(timeUpdate()));//连接信号槽
sleep(3);
timer->start(1000);
2、上一首和下一首播放
ui->listWidget->clear();//清空屏幕的歌词
timer->stop();//歌词时间暂停
song_time=0;//歌曲时间,为全局变量
vecterpos=0;//全局变量
delete lyc;
char buff2[100];
i--;
if(i<0)//第一首不能再加
{
i=0;
}
sprintf(buff2,"%s%s",song_lry_location,song_lry[i]);
lyc = new Lyc(buff2);
ui->label_2->setText(lyc->song_name);//歌曲名
ui->label_5->setText(lyc->song_atuther);//歌手
ui->label_6->setText(lyc->song_album);//专辑名
write(fd,"pt_step -1\n",strlen("pt_step -1\n"));
ui->horizontalSlider_2->setMaximum(lyc->lrcVector.back()->lrc_time);//设置歌曲进度条的最大值
ui->horizontalSlider_2->setMinimum(0);//设置歌曲进度条的最小值
//歌曲进度条的时间
int min = 0;
int sec = 0;
min = song_time/60;
sec = song_time%60;
char mytime[6]={};
sprintf(mytime,"%02d:%02d",min,sec);
this->ui->label_3->setText(mytime);
int timelast = lyc->lrcVector.back()->lrc_time;
min = timelast/60;
sec = timelast%60;
mytime[6]={};
sprintf(mytime,"%02d:%02d",min,sec);
this->ui->label_4->setText(mytime);
timer->start(1000);
下一首
ui->listWidget->clear();
timer->stop();
song_time=0;
vecterpos=0;
delete lyc;
char buff2[100];
i++;
if(i>=j)
{
i=j-1;
}
sprintf(buff2,"%s%s",song_lry_location,song_lry[i]);
lyc = new Lyc(buff2);
ui->label_2->setText(lyc->song_name);//歌曲名
ui->label_5->setText(lyc->song_atuther);//歌手
ui->label_6->setText(lyc->song_album);//专辑名
write(fd,"pt_step 1\n",strlen("pt_step 1\n"));
ui->horizontalSlider_2->setMaximum(lyc->lrcVector.back()->lrc_time);//设置歌曲进度条的最大值
ui->horizontalSlider_2->setMinimum(0);//设置歌曲进度条的最小值
//歌曲进度条的时间
int min = 0;
int sec = 0;
min = song_time/60;
sec = song_time%60;
char mytime[6]={};
sprintf(mytime,"%02d:%02d",min,sec);
this->ui->label_3->setText(mytime);
int timelast = lyc->lrcVector.back()->lrc_time;
min = timelast/60;
sec = timelast%60;
mytime[6]={};
sprintf(mytime,"%02d:%02d",min,sec);
this->ui->label_4->setText(mytime);
timer->start(1000);
3、计时器
song_time++;//每秒++
int min = 0;
int sec = 0;
min = song_time/60;
sec = song_time%60;
char mytime[6]={};
sprintf(mytime,"%02d:%02d",min,sec);
this->ui->label_3->setText(mytime);
ui->horizontalSlider_2->setValue(song_time);
//qDebug()<<song_time;
//qDebug()<<lyc->lrcVector[vecterpos]->lrc_time;
if(song_time>=lyc->lrcVector[vecterpos]->lrc_time)
{
ui->listWidget->clear();
for(int j=vecterpos;j<vecterpos+5;j++)
{
this->item = new QListWidgetItem(lyc->lrcVector[j]->lrc_content);
ui->listWidget->addItem(this->item);
this->item->setTextColor(Qt::black);
this->item->setFont(QFont("宋体",16));
}
vecterpos++;
}
4、歌曲进度条的拖动
timer->stop();
int position = ui->horizontalSlider_2->value();
ui->horizontalSlider_2->setValue(position);
int diff=position - song_time;
song_time=position;
char str[20];
char str1[20]="seek ";
sprintf(str,"%d\n",diff);
strcat(str1,str);
write(fd,str1,strlen(str1));
//歌词播放的回退或者前进
if(diff>0)
{
while(song_time>lyc->lrcVector[vecterpos]->lrc_time)
{
vecterpos++;
}
}
else if(diff<0)
{
while(song_time<lyc->lrcVector[vecterpos]->lrc_time)
{
vecterpos--;
}
}
timer->start(1000);
5、音量的大小
int position = ui->horizontalSlider->value();
ui->horizontalSlider->setValue(position);
volume=position;
char str[20];
char str1[20]="volume ";
sprintf(str,"%d 1\n",volume);
strcat(str1,str);
write(fd,str1,strlen(str1));//主要是通过命令实现的
这些就是这个音乐播放器的大的框架,现在已经把骨架搭好了,还需要自己去填充其他的内容。