实现功能:歌词对应,歌词滚屏显示,歌词总时长,多首歌曲选择播放,进度条(附加动画)
成果演示:代码
主函数代码:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include"start_mplayer/start_mplayer.h"
#include"fun/fun.h"
#include "fun/console.h"
int main(int argc, char const *argv[])
{
while (1)
{
int sim_time = 0;//模拟时钟时间
unsigned long lrc_length = 0;//歌词总长度
char *ret = NULL;//存放所有歌词的地址
char *str[128]={NULL} ;//分行后的歌词地址数组
int line = 0,rea_line = 0;//line为切割分的歌词行数,rea_line 为歌词总时长
LRC *head = NULL;//链表头
char cmd[16]="";//存放指令
char song[32]="";//歌词名
//提示并输入歌曲
clear_screen();
cusor_show();
cusor_moveto(5,15);
printf("歌库现存:简单爱 只因你太美 野狼 雾里看花 烤面筋 Lemon 大花轿\n");//自己添加歌曲
cusor_moveto(5,16);
printf("Please choose the song you want to listen to:\n ");
cusor_moveto(5,17);
scanf("%s",song);
//获取歌词
ret = get_lrc(&lrc_length,ret,song);
if (NULL == ret)
{
cusor_moveto(20,19);
printf("该歌曲尚未收录!!!\n");
cusor_moveto(20,20);
printf("拜拜了宝贝T.T\n");
sleep(2);
cusor_moveto(20,20);
printf("真的要退出了呢 \n");
sleep(2);
cusor_moveto(20,20);
printf("退出成功!!!T.T \n");
sleep(2);
clear_screen();
cusor_show();
return 0;
}
//printf("lrc_length=%lu,%p\n",lrc_length,ret);
//歌词分行
line = cut_lrc(ret,str);
//printf("%d",line);
//歌词存放
head = save_lrc(head,str,line);
rea_line = print_link(head);//获取歌词时长
//printf("%d",rea_line);
//播放歌曲
mplayer_play(song);
//启动模拟时钟,进行输出
sim_clock(head,str,sim_time,rea_line);
//放完一首歌,进行人机交互
cusor_moveto(20,23);
printf("Do you want to keep listening?yes or no\n");
cusor_moveto(20,24);
scanf("%s",cmd);
if (strcmp(cmd,"no")==0)
{
break;
}
else if (strcmp(cmd,"yes")==0)
{
continue;
}
else
{
printf("I don't know,I'm going to default to yes!\n");
}
free(*str);
}
return 0;
}
功能函数:
包括获取歌词,将歌词分行,每句歌词存进链表节点,模拟时钟,查找对应时间的歌词
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include <math.h>
#include"fun.h"
#include "console.h"
因为这里是将歌词放在了一个子文件夹song里,所以获取歌词时候要注意文件地址
/**********************获取歌词******************/
char * get_lrc(unsigned long *length,char *data,char *path)
{
char buf[32]="";
sprintf(buf,"./song/%s.lrc",path);
FILE *fp = NULL;
fp = fopen(buf,"r");
if (NULL == fp)
return NULL;
sscanf(buf,"%[^lrc]",path);
/**********************获取文件长度******************/
fseek(fp,0,2);//流指针指到末尾
*length = ftell(fp);//获取长度
rewind(fp);//指针复位
//申请堆区空间
data = (char *)calloc(1,*length);
if (NULL == data)
{
perror("calloc");
return NULL ;
}
//一次性读取内容
fread(data,*length,1,fp);
//关闭文件
fclose(fp);
return data;
}
strtok函数切割歌词
/**********************歌词分行******************/
int cut_lrc(char *buf,char **str)
{
int i = 0;
//第一次切割
str[i] = strtok(buf, "\r\n");
//第2~n切割
while(str[i] != NULL)//保证上一次切割正常 才有进行下一次切割的必要
{
i++;
str[i] = strtok(NULL,"\r\n");
//while(str[i++] = strtok(str[i],"\r\n"));
}
return i;
}
/**********************歌词存放******************/
/**********************前四行*******************/
void save_lrc_four(char **str)
{
int i = 0;
char tmp[32]="",name[32]="";
for(i=0;i<4;i++)
{
sscanf(str[i],"[%[^:]:%[^]]",tmp,name);
if (strcmp(tmp,"ti") == 0)
{
set_fg_color(COLOR_BLUE);
cusor_moveto(45, 2);
printf("歌名:%s\n",name);
}
else if (strcmp(tmp,"ar") == 0)
{
cusor_moveto(45, 3);
printf("歌手:%s\n",name);
}
else if (strcmp(tmp,"al") == 0)
{
cusor_moveto(45, 4);
printf("专辑:%s\n",name);
}
else if (strcmp(tmp,"by") == 0)
{
cusor_moveto(45, 5);
printf("来自:%s\n",name);
}
}
return;
}
/**********************歌词内容存放******************/
LRC * save_lrc(LRC *head,char **str,int n)
{
LRC pb;
int i = 0;
//歌词输出
for ( i = 4; i < n; i++)
{
char *str_lrc = str[i];
//指向歌词位置
while('[' == *str_lrc)
str_lrc += 10;
//时间用s为单位
char *str_time = str[i];
while ('[' == *str_time)
{
int min = 0,sec = 0,time = 0;
sscanf(str_time,"[%d:%d:%*d]",&min,&sec);
time = min*60+sec;
//printf("%d\n",time);
//时间,歌词一一对应插入链表节点
pb.time = time;
strcpy(pb.lrc,str_lrc);
//有序插入
head = insert_link(head,pb);
str_time += 10;
}
}
return head;
}
有序插入的方法将歌词放入链表储存
/**********************有序插入******************/
LRC * insert_link(LRC *head,LRC pb)
{
LRC *pi = (LRC *)calloc(1,sizeof(LRC));
if (NULL ==pi)
{
perror("calloc");
return head;
}
//pb赋值给pi
*pi = pb;
pi->next = NULL;
if (NULL == head)
{
head = pi;
return head;
}
else
{
LRC *pb = head,*pf = head;
//确定插入点
while (pb->time < pi->time && pb->next != NULL)
{
pf = pb;
pb = pb->next;
}
//插入点判断
if(pb->time >= pi->time)//头部 中部插入
{
if(pb == head)//头部之前插入
{
pi->next = head;
head = pi;
return head;
}
else//中部插入
{
pf->next = pi;
pi->next = pb;
return head;
}
}
else//尾部插入
{
pb->next = pi;
return head;
}
}
}
查找与时间对应的歌词
/**********************查找节点******************/
LRC* search_link(LRC *head, int time)
{
//1、判断链表是否存在
if(head == NULL)//不存在
{
printf("link not found\n");
return NULL;
}
else//链表存在
{
LRC *pb = head;
//逐个将节点中的time 和 time比较 如果不相等 pb=pb->next
while(pb->time != time && pb->next != NULL)
pb = pb->next;
//判断是否找到
if(pb->time == time)//找到
return pb;
else//没找到
return NULL;
}
return NULL;
}
创建一个模拟时钟,模拟时间变化,得到时间寻找歌词
/**********************模拟时钟******************/
void sim_clock(LRC *head,char **str,int time,int end)
{
LRC *ret = NULL;
char buf[12][128]={""};
int n=5, x, y;
char prb[31]="******************************";//30个*
int s = 0;
clear_screen();
cusor_moveto(0,5);
save_lrc_four(str);
while(1)
{
/**********************大风车啊吱悠悠的转******************/
if (time %2 == 0)
{
cusor_moveto(0,3);
for (y = 2 * n + 1; y >= -2 * n - 1; y--)
{
for (x = -2 * n - 1; x <= 2 * n + 1; x++)
{
if (x == 0 && y == 0)putchar(' ');
else if (abs(x) + abs(y) <= 2 * n + 1 && (x==0||y==0||(double)y/x>fabs((double)y/x)/((double)y/x)))putchar('*');
else putchar(' ');
}
putchar('\n');
}
}
else
{
cusor_moveto(0,3);
for (x = 2 * n + 1; x >= -2 * n - 1; x--)
{
for (y = -2 * n - 1; y <= 2 * n + 1; y++)
{
if (y == 0 && x == 0)putchar(' ');
else if (abs(y) + abs(x) <= 2 * n + 1 && (x==0||y==0||(double)y/x>fabs((double)y/x)/((double)y/x)))putchar('*');
else putchar(' ');
}
putchar('\n');
}
}
save_lrc_four(str);//输出四个歌头
cusor_hide();//隐藏光标
/**********************时间******************/
cusor_moveto(29,20);//移动到20行29列
printf("%02d:%02d",time/60,time%60);
/**********************进度条******************/
s=time*sizeof(prb)/end;//end歌曲总长度
prb[s]='>';//*变>
printf("|%s |",prb);//进度条
/**********************歌曲总时长******************/
cusor_moveto(68,20);//移动到20行66列
printf("%02d:%02d",end/60,end%60);
fflush(stdout);
/**********************查找时间对应歌词输出******************/
ret = search_link(head,time);//查找歌词
if (NULL != ret)
{
int i =0;
for(i=0;i<11;i++)
{
strcpy(buf[i],buf[i+1]);
}
strcpy(buf[11],ret->lrc);
for ( i = 0; i < 11; i++)
{
cusor_moveto(36,7+i);
printf("%s ",buf[i]);
}
cusor_moveto(36,18);
set_fg_color(COLOR_RED);
printf("%s ",buf[11]);
set_fg_color(COLOR_BLUE);
fflush(stdout);
if (ret->next==NULL)
{
sleep(5);
break;
}
}
sleep(1);
time++;
}
return;
}
得到歌词的最后一句对应的时间,看做最终时间,但是误差较大
/**********************遍历节点******************/
int print_link(LRC *head)
{
int i = 0;
if(head == NULL)//链表不存在
{
printf("link not find\n");
return 0;
}
else
{
LRC *pb = head;
while(pb != NULL)
{
i=pb->time;
//pb指向下一个节点
pb = pb->next;
}
}
return i+5;
}
功能函数库
#ifndef __FUN_H__
#define __FUN_H__
typedef struct lrc
{
int time;
char lrc[128];
struct lrc *next;
}LRC;
extern void sim_clock(LRC *head,char **str,int time,int end);//模拟时钟 参数time 为模拟时钟时间
extern char * get_lrc(unsigned long *length,char *data,char *path);//获取歌词 参数length 为文件长度 data 为文件内容 返回存文件字符串的空间地址
extern int cut_lrc(char *buf,char **str);//分割歌词 参数buf 为文件内容 str 为切割后的文件
extern LRC * save_lrc(LRC *head,char **str,int n);//保存歌词
extern void save_lrc_four(char **str);//歌头内容
extern LRC * insert_link(LRC *head,LRC pb);//有序插入
extern LRC* search_link(LRC *head, int time);//查找链表
extern int print_link(LRC *head);//遍历链表
#endif
光标定位及颜色改变代码
#include <stdio.h>
#include <stdlib.h>
#include "console.h"
void cusor_moveto(int x, int y)
{// ESC[y;xH
printf("\033[%d;%dH",y,x);
fflush(stdout);
}
//保存光标位置
void cusor_get_pos(void)
{// ESC[s
printf("\033[s");
fflush(stdout);
}
//恢复光标位置
void cusor_set_pos(void)
{// ESC[u
printf("\033[u");
fflush(stdout);
}
//隐藏光标
void cusor_hide(void)
{
printf("\033[?25l");
}
//显示光标
void cusor_show(void)
{
printf("\33[?25h");
}
//清屏
void clear_screen(void)
{// ESC[2J
printf("\033[2J");
fflush(stdout);
}
/*
COLOR_RED 红
COLOR_BLACK 黑
COLOR_GREEN 绿
COLOR_BLUE 蓝
COLOR_YELLOW 黄
COLOR_WHITE 白
COLOR_CYAN 青
COLOR_MAGENTA 洋红
*/
//设置前景颜色
void set_fg_color(int color)
{// ESC[#m
printf("\033[%dm",color);
fflush(stdout);
}
//设置背景颜色
void set_bg_color(int color)
{// ESC[#m
printf("\033[%dm",(color+10));
fflush(stdout);
}
对应的库函数
#ifndef _CONSOLE_H_
#define _CONSOLE_H_
#define COLOR_RED 31
#define COLOR_BLACK 30
#define COLOR_GREEN 32
#define COLOR_BLUE 34
#define COLOR_YELLOW 33
#define COLOR_WHITE 37
#define COLOR_CYAN 36
#define COLOR_MAGENTA 35
/*
COLOR_RED 红
COLOR_BLACK 黑
COLOR_GREEN 绿
COLOR_BLUE 蓝
COLOR_YELLOW 黄
COLOR_WHITE 白
COLOR_CYAN 青
COLOR_MAGENTA 洋红
*/
extern void cusor_moveto(int x, int y);//光标跳转到 y行 x列
extern void cusor_get_pos(void);//保存光标位置
extern void cusor_hide(void);//隐藏光标
extern void cusor_show(void);//显示光标
extern void cusor_set_pos(void);//恢复光标位置
extern void clear_screen(void);//清屏
extern void set_fg_color(int color);//设置字体前景色
extern void set_bg_color(int color);//设置字体背景色
#endif //_CONSOLE_H_
歌曲播放代码,因放在子文件夹中,所以makefi函数里需要标注地址
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include<string.h>
//启动mplayer播放器
//参数song_path 为歌曲的路径
void mplayer_play(char * song_path)
{
song_path = strcat(song_path,"mp3");
pid_t pid;
pid=fork();
if(pid<0)
{
perror("fork");
}
else if(pid==0)
{
close(1);
close(2);
execlp("mplayer","mplayer","-slave","-quiet",song_path,NULL);
exit(0);
}
else
;
}
库函数
#ifndef __START_MPLAYER_H__
#define __START_MPLAYER_H__
//启动mplayer播放器
//参数song_path 为歌曲的路径
extern void mplayer_play(char * song_path);
#endif
makefile函数
exec=main
cc=gcc
obj=main.o ./start_mplayer/start_mplayer.o ./fun/fun.o ./fun/console.o#你要修改的地方
cflags=-Wall -g
$(exec):$(obj)
$(cc) $^ -o $@ $(cflags)
%.o:%.c
$(cc) -c $< -o $@ $(cflags)
clean:
rm $(exec) *.o
总结:麻雀虽小,五脏俱全,虽有不足,然心甚慰,道阻且长,不负年华。
附:程序链接
链接: https://pan.baidu.com/s/10ZVx-ADWE7RNVNL8ys6c3w?pwd=kavn 提取码: kavn 复制这段内容后打开百度网盘手机App,操作更方便哦