该歌词解析器是在Ubuntu系统下实现的,可以实现歌词的滚屏实时显示、进度条及时间显示、以及字符画的显示。
项目截图:
ASCII Generator软件链接:https://pan.baidu.com/s/1spQ5i9fC7kAIwyyJa-aKfg
提取码:13nz
此次项目工作量较大,C语言采取分文件的方式编写;
主要分为4个 .c 文件 1个 .h 文件 还有一个Makefile文件
.c 文件分别为:
1、main.c文件主要是调用各种函数显示出主要的流程;
2、fun.c文件主要包含了各种需要调用的函数实现,包括歌词文件读取、歌词文件切割、歌词分析、启动mplayer播放器、字符画、光标移动、光标隐藏、清屏以及字体颜色设置;
3、link.c主要包含了链表的有序插入、遍历、查询以及释放;
4、display.c包含了最后显示函数的设置。
第一步.将歌词文件读取到堆区空间:
//一次性将文件内容读取到堆区空间
char* get_lrc_memory(char *name[],int num)
{
FILE *fp = NULL;
fp = fopen(name[num-1],"r");
if(fp == NULL)
{
perror("fopen");
return NULL;
}
fseek(fp,0,2);//将流指针定位在文件的尾部
unsigned long len = 0;
len = ftell(fp);//获取文件长度
rewind(fp);
char *arr = NULL;
arr = (char*)calloc(1,len+1);//根据文件长度申请空间
if(arr == NULL)
{
perror("calloc");
return NULL;
}
fread(arr,len,1,fp);//将文件读取到申请的空间中
fclose(fp);
return arr;
}
第二步.将歌词进行切割:
//将读取到的歌词文件 按行进行分割
int strtok_lrc(char *buf[])
{
int i = 0;
while((buf[i++] = strtok(buf[i],"\r\n")));
return i-1;
}
第三步分析歌词
这一步中分为两小步
a.分析歌词的前四行,因为歌词中一般前四行为歌曲信息
//前四行分析 显示
void analyze_lrc_four(char *buf[])
{
int i = 0;
for(i=0;i<4;i++)
{
char tmp1[128] = "";
char tmp2[128] = "";
sscanf(buf[i],"[%[^:]:%[^]]",tmp1,tmp2);
if(strcmp("ti",tmp1)==0)
{
cusor_moveto(45,1);
printf("歌名: %s\n",tmp2);
}
if(strcmp("ar",tmp1)==0)
{
cusor_moveto(45,2);
printf("歌手: %s\n",tmp2);
}
if(strcmp("al",tmp1)==0)
{
cusor_moveto(45,3);
printf("专辑: %s\n",tmp2);
}
if(strcmp("by",tmp1)==0)
{
cusor_moveto(45,4);
printf("制作: %s\n",tmp2);
}
}
}
b.分析后边的歌词并将其按歌词时间顺序存入链表
分析歌词 代码:
//歌词分析
LRC* analyze_lrc_last(char *buf[],const unsigned int row)
{
int i = 4;
LRC *head = NULL;
for(i=4;i<row;i++)
{
char *str_lrc = buf[i];
while(*str_lrc == '[')
{
str_lrc +=10;//str_lrc指向了歌词的位置
}
//逐个时间分析
char *str_time = buf[i];
while(*str_time == '[')
{
int m = 0;
int s = 0;
sscanf(str_time,"[%d:%d.41]", &m,&s);
int time = m*60+s;//以秒为单位
//将时间 和 歌词 一一对应 放入 结构体
LRC tmp;
tmp.time = time;
strcpy(tmp.lrc, str_lrc);
//调用链表的有序插入函数
head = insert_link(head,tmp);
//分析下一个时间
str_time += 10;
}
}
return head;
}
链表插入代码:
//歌词 按照time的大小排序 插入链表
LRC* insert_link(LRC *head,LRC tmp)
{
LRC *pi = (LRC*)calloc(1,sizeof(LRC));
if(pi == NULL)
{
perror("calloc");
return head;
}
*pi = tmp;
pi->next = NULL;
if(head == NULL)//不存在
{
head = pi;
return head;
}
else //存在
{
//a.寻找插入点
LRC *pd = head;
LRC *pf = head;
while(pd->time < pi->time && pd->next != NULL)
{
pf = pd;
pd = pd->next;
}
//b.插入点判断
if(pd->time >= pi->time)//头部中部插入
{
if(pd == head) //头部
{
pi->next = head;
head = pi;
return head;
}
else //中间
{
pi->next = pd;
pf->next = pi;
return head;
}
}
else //尾部插入
{
pd->next = pi;
return head;
}
}
return head;
}
链表插入之后建议进行一次链表遍历操作,以便于确认链表插入没有出现问题:
遍历链表代码如下:
//遍历链表
void print_link(LRC *head)
{
if(head == NULL)
{
printf("link not find\n");
}
else
{
LRC* pb = head;
while(pb != NULL)
{
printf("%d %s\n",pb->time,pb->lrc);
pb = pb->next;
}
}
return;
}
在此之后进行模拟时钟的设计,在同时启动播放器播放音乐,使时钟与音乐时间匹配,便于之后的歌词对比。
然后进行滚屏设计,此处设计一种简单的滚屏设计:
char buf1[128]="";
char buf2[128]="";
char buf3[128]="";
char buf4[128]="";
while(1)
{
//光标定位
cusor_moveto(35,5);
printf("%02d:%02d",time/60,time%60 );
fflush(stdout);
LRC *ret = search_link(head, time);//链表查找 按时间
if(ret != NULL)
{
strcpy(buf1,buf2);
strcpy(buf2,buf3);
strcpy(buf3,buf4);
strcpy(buf4,ret->lrc);
cusor_moveto(40,7);//光标定位
printf("%s ", buf1);
cusor_moveto(40,8);
printf("%s ", buf2);
cusor_moveto(40,8);
printf("%s ", buf3);
cusor_moveto(40,10);
set_fg_color(COLOR_RED);
printf("%s ", buf4);//当前歌词
set_fg_color(COLOR_BLUE);
}
sleep(1);
time++;
}
完成之后可以对项目进行一些创新,比如加入字符画;
此字符画由ASCII Generator软件生成该软件可去网盘下载,链接在文章顶部。
此文章及代码完全由本人完成,以上代码不完全,只包括部分函数。