(一)需求分析

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);//初始化链表1
  search_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_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 <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_ */

GitHub 加速计划 / li / linux-dash
10.39 K
1.2 K
下载
A beautiful web dashboard for Linux
最近提交(Master分支:2 个月前 )
186a802e added ecosystem file for PM2 4 年前
5def40a3 Add host customization support for the NodeJS version 4 年前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐