1.扫描指定路径下的音乐,并显示出来
2.实现音乐的播放、暂停、上一首和下一首的功能
3.程序退出释放内存资源
1.扫描出指定路径下的音乐文件(便利指定文件夹,找出音频文件放在数组里面)
2.创建链表存放音乐的信息(音乐名、歌曲路径、歌曲总数等信息)
3.创建新的进程,用来播放音乐
4.调用kill函数实现音乐的切换和暂停播放功能
注意: 如果出现设备繁忙,可以使用ps -aux命令查看是否正在使用madplay,如果有madplay进程kill掉再运行程序就可以了
函数原型 | 功能介绍 | 头文件 | 备注 |
---|---|---|---|
DIR *opendir(const char *name); | 打开一个目录 | #include #include |
|
struct dirent *readdir(DIR *dirp); | 读取目录的信息放在返回值中 | #include |
|
char *getcwd(char *buf, size_t size) | 获取当前所在路径 | #include |
|
pid_t fork(void) | 创建一个新的进程 | #include |
父进程和子进程 返回值不同 |
int kill(pid_t pid, int sig); | 发送一个信号到进程 | #include |
|
int execlp(const char *file, const char *arg, …0) | 调用系统程序 | #include |
注意和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
#include
#include
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);//初始化链表1
search_file(argv,".mp3");//指定路径下查找指定文件名,结果放在song_info结构体中
for(i=0;inext;
}
/**
* @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_list
start_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
#include
#include
#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_ */