linux下c语言用mplayer实现简单的音乐播放器

蓬思博
2023-12-01

主要功能及实现:

1,播放,暂停及继续,上一页下一页,上一首下一首和退出

2,以简单的并发来实现顺序播放等功能

3,可根据不同格式导入歌曲

4,ANSI控制码实现简单的界面制作

缺点:

1,未处理歌名太长覆盖界面的问题

2,上一页,下一页只经过简单处理,不是很方便

以下只对几个关键代码单独展示,完整源代码见文章末尾:

一、带路径的歌曲名导入链表

利用glob函数解析目录下的音乐名字并导入链表

/* 歌曲路径和名字导入链表 */
static LList_st *loading()
{
	int i, ret;
	musicNode name;
	LList_st *head = NULL;

	head = llist_create(sizeof(name));
	if (head == NULL)
	{
		fprintf(stderr,"llist_create() faild\n");
		exit(1);
	}

	glob(PATTERN,0,NULL,&buff);
	for (i = 0; i < buff.gl_pathc; i++)
	{
		strncpy(name.musicName,buff.gl_pathv[i],NAMESIZE);
		ret = llist_insert(head,&name,BEHIND);
		if (ret < 0)
		{
			fprintf(stderr,"llist_insert() fail\n");		
			exit(1);
		}
	}
	return head;
}

二、创建子进程

fork一个子进程,用子进程实现音乐播放,父进程每秒给alrm_handler发送一个信号,alrm_handler函数使用waitpid函数以非阻塞的方式来给子进程收尸,当子进程正常结束时,播放下一首。

/* 信号函数,接到信号时判断子进程是否正常结束,如果正常结束就播放下一首 */
static void alrm_handler(int s)
{
	int status;
	int ret;

	ret = waitpid(pid,&status,WNOHANG);
	if (ret != 0)
	{
		if (WIFEXITED(status))
		{
			cur = cur->next;
			if (cur == &head->head)
				cur = cur->next;
			strncpy(musicBuff,getNameFromLink(cur->data),MUSICSIZE);
			showList(cur,3);
			fflush(NULL);
			pid = fork();
			createProgress(pid,musicBuff);
		}
	}	
}

/* 创建子进程播放歌曲并将3个标准流置null,每秒向函数发送一个信号 */
void createProgress(pid_t pid,char *musicBuff)
{
	int fd;
	if (pid < 0)
	{
		perror("fork()");
		exit(1);
	}
	if (pid == 0)
	{
		fd = open("/dev/null",O_RDWR);
		if (fd < 0)
		{
			perror("open()");
			exit(1);
		}
		dup2(fd,0);
		dup2(fd,1);
		dup2(fd,2);
		if (fd > 2)
			close(fd);

		execlp("mplayer","mplayer",musicBuff,NULL);
		perror("execlp()");
		exit(1);
	}
	else
	{
		signal(SIGALRM,alrm_handler);
		struct itimerval new = {{1,0},{1,0}};
		setitimer(ITIMER_REAL,&new,NULL);
	}

}

三、主要运行函数


/* 运行函数
 * 关闭系统回显和getchar()以\n结尾的设置
 *
 * flag播放状态 0:未播放 1:正在播放 2:暂停 
 * tmp检测第一次是否创建了子进程,防止意外杀死主进程 0:未创建子进程 1:已创建子进程
 *
 */
void operationFunction()
{
	struct termios new,old;
	int flag = 0;
	int tmp = 0;
	char ch;

	tcgetattr(0,&old);
	tcgetattr(0,&new);

	new.c_lflag = new.c_lflag & ~(ICANON | ECHO);
	new.c_cc[VMIN] = 1;
	new.c_cc[VTIME] = 0;

	tcsetattr(0,TCSANOW,&new);
	printf("\033[?25l");	//隐藏光标

	interface();
	head = loading();
	cur = head->head.next;
	showList(cur,0);
	fflush(NULL);
	
	while(1)
	{
		ch = getchar();
		switch (ch)
		{
			/* 播放 */
			case 'b':
				if (tmp == 0)
				{
					flag = 1;
					if (cur == NULL)
						cur = cur->next;
					strncpy(musicBuff,getNameFromLink(cur->data),MUSICSIZE);
					showList(cur,0);
					fflush(NULL);
					pid = fork();
					tmp = 1;
					createProgress(pid,musicBuff);
				}
				break;
			/* 上一页 */
			case 'w':
				interface();
				showList(cur,2);
				fflush(NULL);
				break;
			/* 下一页 */
			case 's':
				interface();
				showList(cur,1);
				fflush(NULL);
				break;
			/* 上一首 */
			case 'a':
				if (tmp == 1)
				{
					kill(pid,9);
					wait(NULL);
				}
				cur = cur->prev;
				if (cur == &head->head)
					cur = cur->prev;
				strncpy(musicBuff,getNameFromLink(cur->data),MUSICSIZE);
				showList(cur,3);
				fflush(NULL);
				if (tmp == 1)
				{
					pid = fork();
					createProgress(pid,musicBuff);
				}
				break;
			/* 下一首 */
			case 'd':
				if (tmp == 1)
				{
					kill(pid,9);
					wait(NULL);
				}
				cur = cur->next;
				if (cur == &head->head)
					cur = cur->next;
				strncpy(musicBuff,getNameFromLink(cur->data),MUSICSIZE);
				showList(cur,3);
				fflush(NULL);
				if (tmp == 1)
				{
					pid = fork();
					createProgress(pid,musicBuff);
				}
				break;
			/* 暂停 | 恢复播放 */
			case 'p':
				if (flag == 1)
				{
					kill(pid,19);
					flag = 2;
				}
				else if (flag == 2)
				{
					kill(pid,18);
					flag = 1;
				}
				break;
			default:
				break;
		}
		/* 退出 */
		if (ch == 'q')
		{
			if (tmp)
			{
				kill(pid,9);
				wait(NULL);
				break;
			}
			else
				break;
		}
	}

	tcsetattr(0,TCSANOW,&old);	//还原终端原始设置
	printf("\033[?25h\033[2J");

源代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <glob.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <unistd.h>
#include <termios.h>
#include <fcntl.h>
#include <sys/wait.h>
#include "mplayer.h"
#include "double_link.h"

#define PATTERN "/home/ll/Music/*.mp3"
#define MUSICSIZE 256
#define ROW 15
#define LINE 10

/* 音乐名字数组*/
char musicBuff[MUSICSIZE] = {0};	
/* 链表头结点 */
LList_st *head = NULL;
/* 链表当前位置 */
llist *cur = NULL;
/* 进程号 */
pid_t pid;

glob_t buff;
/* 歌曲位置 */
int songPosition = 0;

/* 主界面 */
void interface()
{
	int i, j, k;

	printf("\033[2J\033[5;25H\033[44m--====================--------------------\033[0m\n");
	printf("%c[6;25H",'\033');
	
	for (i = 0; i < ROW; i++)
	{
		printf("\033[44m||\033[0m");
		for (j = 0; j < LINE; j++)
		{
			printf("  ");
		}
		printf("\033[44m||\t\t||\033[0m\n\033[24C");

	}
	
	printf("\033[6;35H%s\n","歌单");
	printf("\033[6;53H%s\n","按键说明");
	printf("\033[8;52H%s\n","播放    'b'");
	printf("\033[9;52H%s\n","暂停    'p'");
	printf("\033[10;52H%s\n","退出    'q'");
	printf("\033[12;52H%s\n","上一首  'a'");
	printf("\033[13;52H%s\n","下一首  'd'");
	printf("\033[15;52H%s\n","上一页  'w'");
	printf("\033[16;52H%s\n","下一页  's'");
	printf("\033[21;25H\033[44m--====================--------------------\033[0m\n");
}

/* 歌曲路径和名字导入链表 */
static LList_st *loading()
{
	int i, ret;
	musicNode name;
	LList_st *head = NULL;

	head = llist_create(sizeof(name));
	if (head == NULL)
	{
		fprintf(stderr,"llist_create() faild\n");
		exit(1);
	}

	glob(PATTERN,0,NULL,&buff);
	for (i = 0; i < buff.gl_pathc; i++)
	{
		strncpy(name.musicName,buff.gl_pathv[i],NAMESIZE);
		ret = llist_insert(head,&name,BEHIND);
		if (ret < 0)
		{
			fprintf(stderr,"llist_insert() fail\n");		
			exit(1);
		}
	}
	return head;
}

/* 从链表中获取歌曲名字 */
static char *getNameFromLink(void *record)
{
	musicNode *dest = record;
	return dest->musicName;
}

/* 获取不包含路径的名字 */
static char *getRealName(char *name)
{
	char *pos = NULL;

	pos = strrchr(name,'/');
	if (pos == NULL)
		return name;
	return pos+1;
}

/* 歌单展示
 *tmp为1时,下一页
 *tmp为2时,上一页
 *当当前链表歌曲和播放歌曲一样时,以红色背景色显示
 *
 */
void showList(llist *cur,int tmp)
{
	int i, k;
	
	printf("\033[8;31H");
	if (tmp == 1)
	{
		songPosition += ROW-3;
	}
	if (tmp == 2)
	{
		songPosition -= ROW-3;
	}

	for (i = songPosition, k = 0; i < buff.gl_pathc && i <= ROW-2; i++,k++)
	{
		if (k < ROW-3)
		{
			if (strcmp(buff.gl_pathv[i],getNameFromLink(cur->data)) == 0)
				printf("\033[41m%s\033[0m\n\033[30C",getRealName(buff.gl_pathv[i]));
			else
				printf("%s\n\033[30C",getRealName(buff.gl_pathv[i]));
		}
	}
}

/* 信号函数,接到信号时判断子进程是否正常结束,如果正常结束就播放下一首 */
static void alrm_handler(int s)
{
	int status;
	int ret;

	ret = waitpid(pid,&status,WNOHANG);
	if (ret != 0)
	{
		if (WIFEXITED(status))
		{
			cur = cur->next;
			if (cur == &head->head)
				cur = cur->next;
			strncpy(musicBuff,getNameFromLink(cur->data),MUSICSIZE);
			showList(cur,3);
			fflush(NULL);
			pid = fork();
			createProgress(pid,musicBuff);
		}
	}	
}

/* 创建子进程播放歌曲并将3个标准流置null,每秒向函数发送一个信号 */
void createProgress(pid_t pid,char *musicBuff)
{
	int fd;
	if (pid < 0)
	{
		perror("fork()");
		exit(1);
	}
	if (pid == 0)
	{
		fd = open("/dev/null",O_RDWR);
		if (fd < 0)
		{
			perror("open()");
			exit(1);
		}
		dup2(fd,0);
		dup2(fd,1);
		dup2(fd,2);
		if (fd > 2)
			close(fd);

		execlp("mplayer","mplayer",musicBuff,NULL);
		perror("execlp()");
		exit(1);
	}
	else
	{
		signal(SIGALRM,alrm_handler);
		struct itimerval new = {{1,0},{1,0}};
		setitimer(ITIMER_REAL,&new,NULL);
	}

}

/* 运行函数
 * 关闭系统回显和getchar()以\n结尾的设置
 *
 * flag播放状态 0:未播放 1:正在播放 2:暂停 
 * tmp检测第一次是否创建了子进程,防止意外杀死主进程 0:未创建子进程 1:已创建子进程
 *
 */
void operationFunction()
{
	struct termios new,old;
	int flag = 0;
	int tmp = 0;
	char ch;

	tcgetattr(0,&old);
	tcgetattr(0,&new);

	new.c_lflag = new.c_lflag & ~(ICANON | ECHO);
	new.c_cc[VMIN] = 1;
	new.c_cc[VTIME] = 0;

	tcsetattr(0,TCSANOW,&new);
	printf("\033[?25l");	//隐藏光标

	interface();
	head = loading();
	cur = head->head.next;
	showList(cur,0);
	fflush(NULL);
	
	while(1)
	{
		ch = getchar();
		switch (ch)
		{
			/* 播放 */
			case 'b':
				if (tmp == 0)
				{
					flag = 1;
					if (cur == NULL)
						cur = cur->next;
					strncpy(musicBuff,getNameFromLink(cur->data),MUSICSIZE);
					showList(cur,0);
					fflush(NULL);
					pid = fork();
					tmp = 1;
					createProgress(pid,musicBuff);
				}
				break;
			/* 上一页 */
			case 'w':
				interface();
				showList(cur,2);
				fflush(NULL);
				break;
			/* 下一页 */
			case 's':
				interface();
				showList(cur,1);
				fflush(NULL);
				break;
			/* 上一首 */
			case 'a':
				if (tmp == 1)
				{
					kill(pid,9);
					wait(NULL);
				}
				cur = cur->prev;
				if (cur == &head->head)
					cur = cur->prev;
				strncpy(musicBuff,getNameFromLink(cur->data),MUSICSIZE);
				showList(cur,3);
				fflush(NULL);
				if (tmp == 1)
				{
					pid = fork();
					createProgress(pid,musicBuff);
				}
				break;
			/* 下一首 */
			case 'd':
				if (tmp == 1)
				{
					kill(pid,9);
					wait(NULL);
				}
				cur = cur->next;
				if (cur == &head->head)
					cur = cur->next;
				strncpy(musicBuff,getNameFromLink(cur->data),MUSICSIZE);
				showList(cur,3);
				fflush(NULL);
				if (tmp == 1)
				{
					pid = fork();
					createProgress(pid,musicBuff);
				}
				break;
			/* 暂停 | 恢复播放 */
			case 'p':
				if (flag == 1)
				{
					kill(pid,19);
					flag = 2;
				}
				else if (flag == 2)
				{
					kill(pid,18);
					flag = 1;
				}
				break;
			default:
				break;
		}
		/* 退出 */
		if (ch == 'q')
		{
			if (tmp)
			{
				kill(pid,9);
				wait(NULL);
				break;
			}
			else
				break;
		}
	}

	tcsetattr(0,TCSANOW,&old);	//还原终端原始设置
	printf("\033[?25h\033[2J");

}

 类似资料: