主要功能及实现:
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");
}