(一)需求分析
1.扫描指定路径下的音乐,并显示出来
2.实现音乐的播放、暂停、上一首和下一首的功能
3.程序退出释放内存资源
(二)思路
1.扫描出指定路径下的音乐文件(便利指定文件夹,找出音频文件放在数组里面)
2.创建链表存放音乐的信息(音乐名、歌曲路径、歌曲总数等信息)
3.创建新的进程,用来播放音乐
4.调用kill函数实现音乐的切换和暂停播放功能
注意: 如果出现设备繁忙,可以使用ps -aux命令查看是否正在使用madplay,如果有madplay进程kill掉再运行程序就可以了
(三)接口函数
函数原型 | 功能介绍 | 头文件 | 备注 |
---|---|---|---|
DIR *opendir(const char *name); | 打开一个目录 | #include <sys/types.h> #include <dirent.h> | |
struct dirent *readdir(DIR *dirp); | 读取目录的信息放在返回值中 | #include <dirent.h> | |
char *getcwd(char *buf, size_t size) | 获取当前所在路径 | #include <unistd.h> | |
pid_t fork(void) | 创建一个新的进程 | #include <unistd.h> | 父进程和子进程 返回值不同 |
int kill(pid_t pid, int sig); | 发送一个信号到进程 | #include <signal.h> | |
int execlp(const char *file, const char *arg, …0) | 调用系统程序 | #include <unistd.h> | 注意和system区别 |
(四)功能演示
在使用前先安装madplay音频解码器,在Ubuntu下安装如下:apt install madplay
安装完成后,编译工程(make) 之后运行程序传递参数就可以播放音乐了
输入1测试:
剩下的我就不一一测试了,感兴趣的可以自己下载工程测试下
(五)代码实现
这里贴出主要代码,因为我把功能开写的代码有些多:
music.c
/*********************************************************************
* *
File : music.c *
Purpose : play music *
author : @Mrming *
Time : 2020/2 *
***************************END-OF-HEADER******************************
*/#include "link.h"
#include "file.h"
#include <unistd.h>
#include <string.h>
#include <signal.h>
char CtrFlag=0;//0从头播放 1播放 2暂停
pid_t pid;//播放音乐进程句柄
extern struct song song_info;//歌曲信息结构体
link_t song_list = { NULL,0,"song_list" };//创建链表,存放歌曲的信息(链表头部,节点个数,链表名字)
node *cur_song= NULL;//当前歌曲节点/*** @brief 实现音乐的控制播放、暂停** @param None**/
void play()
{char *str=NULL;switch(CtrFlag){case 0:{pid = fork();if(pid==0){str = strcat(cur_song->path,cur_song->name);//printf("\n%s\n",str);close(0);//execlp("madplay","madplay",str,"-r","-q",NULL);//execlp会开辟一个新的线程覆盖掉子线程}CtrFlag = 2; }break;case 1:kill(pid,18);//对于主进程控制继续播放CtrFlag = 2; break;case 2:{CtrFlag = 1;if(pid>0) {kill(pid,19);//主进程中暂停子进程}}break;default:printf("play:");}}/*** @brief 获取下一首歌曲并播放** @param None**/
void next()
{cur_song = next_node(cur_song);if(cur_song == NULL){cur_song = song_list.phead;}kill(pid,9);//杀死当前音乐的进程CtrFlag = 0;//恢复默认值play();//播放当前选中的音乐
}/*** @brief 获取上一首歌曲并播放** @param None**/
void prev()
{cur_song = prev_node(cur_song);if(cur_song->prev == NULL){cur_song = song_list.phead->next;printf("this song is the first! \n");}kill(pid,9);//杀死当前音乐的进程CtrFlag = 0;//恢复默认值play();//播放当前选中的音乐
}/*** @brief 显示音乐列表** @param None**/
void show_music_list(void)
{printf("歌曲总数:%d首\n",song_info.song_num);//打印歌曲总数out_list(&song_list);
}/*** @brief 关闭音乐释放链表资源** @param None**/
void source_recovery(void)
{system("killall madplay");//关闭madplay进程destory_link(&song_list);//退出程序前释放资源
}
//打印当前歌曲的上一首和下一首
void near_curMusic(node *cur_song)
{printf("\n当前歌曲 : %s\n",cur_song->name);if(CtrFlag == 2)printf("播放状态 :播放\n");if(CtrFlag == 1)printf("播放状态 :暂停\n");}/*** @brief 查找音乐文件放在song_list中** @param argv:路径**/
void search_all_music(char *argv)
{int i = 0;link_init(&song_list);//初始化链表1search_file(argv,".mp3");//指定路径下查找指定文件名,结果放在song_info结构体中for(i=0;i<song_info.song_num;i++)//歌曲信息插入链表{link_insert_tail(&song_list,song_info.song_name[i],song_info.song_path[i]);//尾插数据//printf("%s\t%s\n",song_info.song_name[i],song_info.song_path[i]);}cur_song = song_list.phead->next;
}/*** @brief 功能列表** @param None**/
int menu()
{int i;printf("+-------------------------------------------------------------+\n");printf("| | \n");printf("| Welcome to MP3 PLAYER | \n");printf("| | \n");printf("| 1.Play\\stop | \n");printf("| 2.prev song | \n");printf("| 3.next song | \n");printf("| 4.music list | \n");printf("| 5.quit | \n");printf("|_____________________________________________________________| \n");printf("choose->");scanf("%d",&i);return i;
}/*** @brief 功能选择** @param None**/
void start_music()
{show_music_list();while(1){near_curMusic(cur_song);switch(menu()){case 1:play();break;case 2:prev();break;case 3:next();break;case 4 :show_music_list();break;case 5 :return;default:printf("重新输入!\n");break;}}
}int main(int argc, char *argv[])
{system("clear");search_all_music(argv[1]);//查找目录下的mp3文件,结果放在song_liststart_music();//播放音乐source_recovery();return 0;
}
file.c
#include "file.h"
/*************************************函数功能: 扫描目录下指定后缀名的文件*输入 : 路径*输出 : 扫描成功输出0,扫描失败输出-1*注释 : 扫描到的文件名放在song_info结构体里面*************************************/
struct song song_info={0,0};int search_file(char* name,char * type)
{DIR *p=NULL;struct dirent *pdir;p=opendir(name);if(p==NULL){perror("open dir error");return -1;}chdir(name);while(pdir=readdir(p)){if(!(strcmp(pdir->d_name,".")==0) && !(strcmp(pdir->d_name,"..")==0)){struct stat st;stat(pdir->d_name,&st);if((st.st_mode &S_IFMT)== S_IFDIR){// printf("this is a content:%s\n",pdir->d_name);//system("pwd");search_file(pdir->d_name,"mp3");}else {char * file_type;file_type = strstr(pdir->d_name,type);if(file_type){getcwd(song_info.song_path[song_info.song_num],sizeof(song_info.song_path[0]));sprintf(song_info.song_name[song_info.song_num++],"/%s",pdir->d_name);}}}}chdir("..");closedir(p);
return 0;
}
link.h
#pragma once
#ifndef _LINK_H_
#define _LINK_H_ #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define name_max 255
typedef struct _node
{char name[name_max];char path[name_max];struct _node * prev;struct _node * next;
}node;typedef struct _link
{node *phead;//链表头部int count;//节点个数char name[name_max];//链表名字
}link_t;int link_init(link_t *link);//链表初始化,成功返回0,失败返回-1
int link_insert_tail(link_t *link, char *dat1,char *dat2);//尾插链表,使用前先执行link_init初始化链表
int destory_link(link_t *link);//删除链表,成功返回0 失败返回-1
node * pos_list(link_t link, int n);//定位到链表任意位置
node * next_node(node * node);//获取当前节点的上一个节点
node * prev_node(node * node);//获取当前节点的下一个节点
void out_list(link_t *head);//遍历链表#endif /* ifndef _MUSIC_H_ */