在Linux下的基于myplayer实现的歌曲播放器

一、基础原理

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));//主要是通过命令实现的
    这些就是这个音乐播放器的大的框架,现在已经把骨架搭好了,还需要自己去填充其他的内容。
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值