在linux系统下使用madplay制作音乐播放器

知识共享许可协议 版权声明:署名,允许他人基于本文进行创作,且必须基于与原先许可协议相同的许可协议分发本文 (Creative Commons

目的

制作一个可以在Linux环境下运行的MP3播放器。要求实现的MP3主要功能包括:播放、暂停、继续、停止、循环播放、上一曲、下一曲、退出、歌曲显示等功能。
本设计是基于madplay库实现的,程序运行时会显示一个字符界面,在该字符界面下输入相应的提示字符即可实现音乐的播放、暂停、继续、停止、上一曲、下一曲、歌词的显示等功能,也可以加入额外的一些功能。编写一个MakeFile文件,make命令来实现程序的编译。
环境搭建
Madplay是一个开源mp3解码库,对mp3解码算法做了很多优化。常用的安装环境步骤如下
创建依赖库->解压工具软件包->move可执行文件->设置环境变量
Ubuntu环境下直接执行sudo apt-get install madplay可以直接在线安装

抽象数据类型定义
1 音乐文件的存储采用双向链表的结构体music表示,列表单由music_list表示。
2 音乐文件的本机读取通过readFileList函数实现,定义char类型数组musicFilePath存放文件路径。
3 通过musicFind()函数返回音乐所在位置,在主函数的getMusicDir()有所应用,定义控制指针*pos实现上下首的播放切换。
4 使用madplay命令基础操作,定义进程pid控制。
5 定义arg[]数组存放指令的参数,定义command字符变量(char类型)来存放控制指令。

详细设计
封装madplay功能

int play(char *name)
{
    pid_t pid;
    int status;
    if((pid = fork()) < 0)
    {
        perror("Fork error\n");
        return 0;
    }
    else if(0 == pid)
    {
        waitpid(getppid(), &status, 0); // 等待父进程结束
        execlp("madplay", "madplay","-q", name, (char*)0);
        return 0;
    }
    else
    {
        return pid;    
    }
}

int suspend(pid_t pid)
{
    kill(pid, SIGSTOP);
    return 0;
}

int continuePlay(pid_t pid)
{
    kill(pid, SIGCONT);
    return 0;
}

int end(pid_t pid)
{
    kill(pid, SIGINT);
    return 0;
}

歌曲列表

music_list * createMusicList()
{
    music_list *mList;
    mList = (music_list *)malloc(sizeof(music_list));
    if(NULL == mList)
    {
        perror("fail\n");
        exit(1);
    }
    mList->length = 0;
    mList->mHead = NULL;

    return mList;
}
//歌单显示函数
int musicListDisplay(music_list *mList)
{
    music *tmp;
    tmp = mList->mHead;
    printf("%s\n", tmp->name);
    int i;
    for(i = 1; i < mList->length; ++i)
    {
        tmp = tmp->next;
        printf("%s\n", tmp->name);
    }
    printf("\n\n");
    return 0;
}

从目录中寻找mp3文件插入到音乐列表中

int readFileList(const char *basePath, music_list *mList)
{
    DIR *dir;
    struct dirent *ptr;  
    if((dir = opendir(basePath)) == NULL)
    {
        perror("error.\n");
        exit(1);
    }

    while((ptr = readdir(dir)) != NULL)
    {
        if(8 == ptr->d_type) // 文件
        {
            int i = 0;
            int flag = 0;
            while('\0' != ptr->d_name[i])
            {
                if('.' == ptr->d_name[i])
                {
                    if(0 != flag)
                        flag = 0;
                    ++flag;
                }
                else if(1 == flag && 'm' == ptr->d_name[i])
                    ++flag;
                else if(2 == flag && 'p' == ptr->d_name[i])
                    ++flag;
                else if(3 == flag && '3' == ptr->d_name[i])
                {
                    if('\0' ==  ptr->d_name[i + 1]) // 是mp3文件
                    {
                        musicListInsert(mList, ptr->d_name);
                        break;
                    }
                    flag = 0;
                }
                else if(0 != flag)
                    flag = 0;
                ++i;
            }
        }
    }
    closedir(dir);
    return 0;
}

获取音乐文件路径函数

int getMusicDir(music_list *mList, char *name, int *pos, int nextLast)
{
    if(0 == nextLast) // 由name指定的歌
    {   
        *pos = musicFind(mList, name); // 文件在链表中位置
        if(-1 == *pos) // 文件不存在
        return -1;
    }    
    else
    {
        music *tmp;
        tmp = locateMusic(mList, *pos); // 指向当前播放的歌曲

        if(1 == nextLast) // 下一首
        {
            strcpy(name, tmp->next->name);
            ++(*pos);
        }
        else // 上一首
        {
            strcpy(name, tmp->prev->name);
            --(*pos);
        }
    }
    char path[256] = "./music/";
    strcat(path, name);
    strcpy(name, path);
    return 0;
}

读指令函数

char getCommand(char *arg)
{
    char res = '0'; // 指令
    char input[256];
    printf("请进行操作: ");
    fgets(input, 256, stdin);
    input[strlen(input) - 1] = '\0';
    int i = 0;
    while(' ' == input[i]) // 去除前空格
        ++i;
    if('\0' != input[i]) // fget会在字符串尾加'\n'
        res = input[i];  // 获得第一各有效字符    
    if('s' == res || 'S' == res)
    {
	strcpy(arg, "\0");
        if(' ' != input[++i] || '\0' == input[i]) // 为非空格或无参数
            return 'E';
        ++i;
        strcat(arg, &input[i]);
    }
    else // 无参数指令,检测指令是否有错
    {
        while('\0' != input[++i])
        {
            if(' ' != input[i])
            {
                res = 'E';
                break;
            }
        }
    }
    return res;
}

指令执行函数

int execCommand(char command, char *arg, pid_t *pid, music_list *mList, int *pos, int *quit)
{   
    switch(command)
    {

        case 's':
            if(sCommand(arg, pid, mList, pos) == -1)
            printf("播放失败!\n");
            break;
        case 'u':
            if(uCommand(pid, mList, pos) == -1)
                printf("没有音乐正在播放!\n");
            break;
        case 'n':
            nCommand(pid, mList, pos);
            break;
        case 'c':
            cCommand(pid);
            break;
        case 'p':
            pCommand(pid, mList, pos);
            break;
        case 'q':
            qCommand(pid, pos, quit);
            break;
        default:
            printf("无效的命令!\n");
    }  
    return 0;
}

重写的播放暂停等功能函数

int sCommand(char *arg, pid_t *pid, music_list *mList, int *pos)
{
    getMusicDir(mList, arg, pos, 0); // arg为音乐文件路径
    if(-1 == *pos) // 文件不存在
    {
        printf("文件不存在!\n");
        return -1;
    }
    else
    {
        *pid = play(arg);
        printf("正在播放中...\n");
        return 0;
    }
}
int uCommand(pid_t *pid, music_list *mList, int *pos)
{
    if(-1 == *pos) // 无音乐播放
        return -1;
    else
    {
	printf("音乐被暂停播放...\n");
        suspend(*pid);
        return 0;
    }
}
int cCommand(pid_t *pid)
{
    printf("被暂停的音乐继续播放中...\n");
    continuePlay(*pid);
    return 0;
}
int nCommand(pid_t *pid, music_list *mList, int *pos)
{
    if(-1 != *pid)
        end(*pid);
    char name[256];
    getMusicDir(mList, name, pos, 1);
    *pid = play(name);
    printf("正在播放中...\n");
    return 0;
}
int pCommand(pid_t *pid, music_list *mList, int *pos)
{
    if(-1 != *pos) // 结束未结束的播放
        end(*pid);
    char name[256];
    getMusicDir(mList, name, pos, 2);
    *pid = play(name);
    printf("正在播放中...\n");

    return 0;
}
int qCommand(pid_t *pid, int *pos, int *quit)
{
    if(-1 != *pos) // 结束未结束的播放
        end(*pid);
    *quit = 1;
    return 0;
}

主函数

int main()
{
    char command;         
    char arg[256];            
    char musicFilePath[256]; 
    music_list *mList;        
    int *pos;             // 当前播放的音乐在链表中的位置
    pos = (int *)malloc(sizeof(int));
    *pos = -1;
    pid_t *pid;                // 音乐进程pid
    pid = (pid_t *)malloc(sizeof(pid_t));
    int *quit;
    quit = (int *)malloc(sizeof(int));
    *quit = 0;
    mList = createMusicList();      
    getMusicFileDir(musicFilePath);    
    readFileList(musicFilePath, mList); 
    printf("------------------欢迎来到 MP3播放器----------------\n\n");
    printf(" 制作者:刘金泽 邢育瑄         \n");
    printf(" 该播放器支持MP3类型的音乐文件.\n");
    musicListDisplay(mList);
    printf("\n");
    displayHelp();
    printf("\n");
    while(0 == *quit)
    {
        command = getCommand(arg);         // 读指令
        execCommand(command, arg, pid, mList, pos, quit);  // 执行指令
    }
    return 0;
}

make之后运行可执行文件
演示效果如下
在这里插入图片描述
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_44611644/article/details/95061974