我们要明确目的,实现歌曲歌词同步。

1.将歌词文件一次性去取到内存中。(以周董的“简单爱”为例)

a.用fopen打开歌词文件 FILE *fp  = fopen(“简单爱.lrc”,"r");(r->只读)

b.使用fseek将文件流指针,要定位到文件尾部,ftell或得文件总大小;

c.使用rewind 复位文件流指针;

d.根据文件总大小从堆区申请合适的空间;

e.使用fread读取文件数据到内存中;

2.将arr指向的内存数据,按行“\r\n”切割,并存入字符指针数组 char *buf[128] = {NULL};

 strtok函数切割
//arr指向内存存储的歌词
char *buf[]={arr,NULL};
int i=;
//切割
while(buf[i++] = strtok(buf[i],"\r\n"));

已将歌词的每一行 存放 在 指针数组中 注意 记得保存 切割到的行数

3.逐行分析buf[0]代表第0行,buf[n]代表第n行

a.先单独分析前四行

原数据

[ti:简单爱]
[ar:周杰伦]
[al:范特西]
[by:大脸猫]

要得到的结果:

Ubuntu下实现歌词解析-LMLPHP

 int i=;
for(i=;i<;i++)
{
char tmp[]="";
//[ti:简单爱] -->sscanf(buf[0],"%*[^:]:%[^]]", tmp);//tmp--简单
buf[i];//分析
}

b、逐行分析剩下的行 将时间+歌词 一一对应 插入链表

链表节点的设计:

 typedef struct lrc
{
//数据域
int time;
char lrc[]; //指针域
struct lrc *next;
}LRC;

4.模拟计时器 i++

计时器每走一秒,都会对链表中成员域(time)进行比较,时间相等,则输出成员域歌词(lrc_sentence)

5、滚屏(4行)

//光标定位:(注意)

6、反显 当前歌词为 红色 其他歌词为黑色

7、启动mplayer

6和7步骤都可以直接调用别人的API接口,现在我贴出代码,只可意会不可言传~~~细品

main.c

 #include "lrc.h"
#include "console.h"
#include "start_mplayer.h"
#include <unistd.h> int main(int argc, char *argv[])
{
char *lrc_condent = NULL;
LRC *head = NULL;
char *arr[]={lrc_condent,NULL};
int i = ; lrc_condent = read_song_lrc("./lrc/简单爱.lrc");
//printf("%s",lrc_condent);
arr[] = lrc_condent;
//切割每一行
while(arr[i++] = strtok(arr[i],"\r\n")); head = lrc_handle(arr, head); mplayer_play("./song/love.mp3");
lrc_print(head); return ;
}

lrc.c

 #include<stdlib.h>
#include <unistd.h>
#include "lrc.h"
#include"console.h"
LRC *lrc_create_head()
{
LRC *head = (LRC*)calloc(, sizeof(LRC));
head->next = NULL;
return head;
}
//取出歌词
char *read_song_lrc(char *lrc_name)
{
FILE *fp = fopen(lrc_name,"r");
char *lrc_content = NULL;
unsigned long int length;
if(fp == NULL)
{
perror("fopen");
return NULL;
} fseek(fp,,);
length = ftell(fp);
rewind(fp);
lrc_content = (char *)calloc(,length);
if (lrc_content == NULL)
{
perror("calloc");
return NULL;
} fread(lrc_content,length,,fp);
fclose(fp); return lrc_content;
}
//分割歌词
void lrc_strtok(char *lrc_condent)
{
char *arr[]={lrc_condent,NULL};
int i = ; while((arr[i++] = strtok(arr[i],"\r\n")));
i= ;
while(arr[i] != NULL)
printf("%s\n", arr[i++]);
}
//顺序插入
LRC * lrc_insert(LRC *head, LRC tmp)
{
//1、给待插入的节点pi 申请 堆区空间
LRC *pi = (LRC *)calloc(,sizeof(LRC));
if(pi == NULL)
{
perror("calloc");
return head;
} //2、将tmp的内容 赋值给 *pi
*pi = tmp;
pi->next = NULL; //3、链表节点pi的插入
if(head == NULL)//链表不存在
{
head = pi;
return head;
}
else//存在
{
//a、寻找插入点
LRC *pb = head, *pf = head;
while(pb->time < pi->time && pb->next != NULL)
{
pf = pb;
pb = pb->next;
} //b、插入点的判断
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;
}
}
return head;
}
//处理歌词
LRC * lrc_handle(char *buf[], LRC *head)
{
int i = ;
//开头的歌手,编曲等字符串
char *song_infor[] = {"ti", "ar", "al", "by"}; system("clear");
while(buf[i] != NULL)
{
char flag = ;
sscanf(buf[i],"%*c%c", &flag);
if(flag >= '' && flag <= '')//除了前四行
{
char *str_lrc = buf[i], *str_time = buf[i];
//每遇到"["判断是否这句歌词出现的次数
while(*str_lrc == '[')
str_lrc += ; while(*str_time == '[')
{
LRC temp;
int minute = , second = , ms = ;
//取出每个节点的时分秒
sscanf(str_time, "[%d:%d.%d]", &minute, &second, &ms);
//统一单位到ms
temp.time = minute * + second + ((ms > )?:);//ms用四舍五入
//把歌词内容赋值到该次链表的lrc_sentence
strcpy(temp.lrc_sentence, str_lrc);
//循环把时间插入链表节点
head = lrc_insert(head, temp);
str_time += ;
}
} else
{
//拆分前四行的,拿到歌曲名等 char tmp[]="";
sscanf(buf[i], "%*[^:]:%[^]]", tmp);
if(i==)
{
cusor_moveto(,);
printf("%s\n", tmp);
}
else if(i==)
{
cusor_moveto(,);
printf("%s\n", tmp);
}
else if(i==)
{
cusor_moveto(,);
printf("%s\n", tmp);
}
else if(i==)
{
cusor_moveto(,);
printf("%s\n", tmp);
}
}
i++;
} return head;
}
//歌词滚屏
int times = ;
int screen = ; LRC* lrc_print(LRC *head)
{
int time=;
char buf1[]="";
char buf2[]="";
char buf3[]="";
char buf4[]="";
while()
{
//光标定位
cusor_moveto(,);
printf("%02d:%02d",time/,time% );
fflush(stdout); LRC *ret = search_link(head, time);
if(ret != NULL)
{
//滚起来
strcpy(buf1,buf2);
strcpy(buf2,buf3);
strcpy(buf3,buf4);
strcpy(buf4, ret->lrc_sentence); cusor_moveto(,);
printf("%s ", buf1); cusor_moveto(,);
printf("%s ", buf2); cusor_moveto(,);
printf("%s ", buf3); cusor_moveto(,);
set_fg_color(COLOR_RED);
printf("%s ", buf4);//当前歌词
set_fg_color(COLOR_BLACK );
fflush(stdout);
}
//滚屏显示歌词
//第一行 sleep();
time++;
} } LRC* search_link(LRC *head, int time)
{
//1、判断链表是否存在
if(head == NULL)//不存在
{
printf("link not found\n");
return NULL;
}
else//链表存在
{
LRC *pb = head; //逐个将节点中的name 和 name比较 如果不相等 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 mplayer_play(char * song_path)
{
pid_t pid;
pid=fork();
if(pid<)
{
perror("fork");
}
else if(pid==)
{
close();
close();
execlp("mplayer","mplayer","-slave","-quiet",song_path,NULL);
exit();
}
else;
}

lrc.h

 #ifndef __LRC_H__
#define __LRC_H__ #include <stdio.h>
#include <stdlib.h>
#include <string.h> typedef struct lrc
{
int time;
char lrc_sentence[]; struct lrc *next;
}LRC;
//创造链表头
LRC *lrc_create_head(void);
//顺序插入
LRC * lrc_insert(LRC *head, LRC tmp);
//读取歌词
char *read_song_lrc(char *lrc_name);
//切割歌词
void lrc_strtok(char *lrc_condent);
//处理歌词
LRC * lrc_handle(char *buf[], LRC *head);
//遍历歌词
LRC* lrc_print(LRC *head);
extern LRC* search_link(LRC *head, int time);
void mplayer_play(char * song_path); #endif

----------------------好的,我的第一阶段项目总结完了,写的不好,还请见谅----------------------

05-22 15:47