Ubuntu下实现歌词解析

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

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};

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

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

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

a.先单独分析前四行

原数据

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

要得到的结果:

 

 

 

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

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

链表节点的设计:

1 typedef struct lrc
2 {
3      //数据域
4      int time;
5      char lrc[128];
6      
7      //指针域
8      struct lrc *next; 
9 }LRC;    

4.模拟计时器 i++

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

5、滚屏(4行)

//光标定位:(注意)

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

7、启动mplayer

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

main.c

 1 #include "lrc.h"
 2 #include "console.h"
 3 #include "start_mplayer.h"
 4 #include <unistd.h>
 5 
 6 int main(int argc, char *argv[])
 7 {
 8     char *lrc_condent = NULL;
 9     LRC *head = NULL;
10     char *arr[128]={lrc_condent,NULL};
11     int i = 0;
12     
13     lrc_condent = read_song_lrc("./lrc/简单爱.lrc");
14     //printf("%s",lrc_condent);
15     arr[0] = lrc_condent;
16     //切割每一行
17     while(arr[i++] = strtok(arr[i],"\r\n"));
18 
19     head = lrc_handle(arr, head);
20 
21     
22     mplayer_play("./song/love.mp3");
23     lrc_print(head);
24     
25     
26     return 0;
27 }

lrc.c

  1 #include<stdlib.h>
  2 #include <unistd.h>
  3 #include "lrc.h"
  4 #include"console.h"
  5 LRC *lrc_create_head()
  6 {
  7     LRC *head = (LRC*)calloc(1, sizeof(LRC));
  8     head->next = NULL;
  9     return head;
 10 }
 11 //取出歌词
 12 char *read_song_lrc(char *lrc_name)
 13 {
 14     FILE *fp = fopen(lrc_name,"r");
 15     char *lrc_content = NULL;
 16     unsigned long int length;
 17     if(fp == NULL)
 18     {
 19         perror("fopen");
 20         return NULL;
 21     }
 22 
 23     fseek(fp,0,2);
 24     length = ftell(fp);
 25     rewind(fp);
 26     lrc_content = (char *)calloc(1,length);
 27     if (lrc_content == NULL)
 28     {
 29         perror("calloc");
 30         return NULL;
 31     }
 32 
 33     fread(lrc_content,length,1,fp);
 34     fclose(fp);
 35 
 36     return lrc_content;
 37 }
 38 //分割歌词
 39 void lrc_strtok(char *lrc_condent)
 40 {
 41     char *arr[128]={lrc_condent,NULL};
 42     int i = 0;
 43 
 44     while((arr[i++] = strtok(arr[i],"\r\n")));
 45     i= 0;
 46     while(arr[i] != NULL)
 47         printf("%s\n", arr[i++]);
 48 }
 49 //顺序插入
 50 LRC * lrc_insert(LRC *head, LRC tmp)
 51 {
 52     //1、给待插入的节点pi 申请 堆区空间
 53     LRC *pi = (LRC *)calloc(1,sizeof(LRC));
 54     if(pi == NULL)
 55     {
 56         perror("calloc");
 57         return head;
 58     }
 59 
 60     //2、将tmp的内容 赋值给 *pi
 61     *pi = tmp;
 62     pi->next = NULL;
 63 
 64     //3、链表节点pi的插入
 65     if(head == NULL)//链表不存在
 66     {
 67         head = pi;
 68         return head;
 69     }
 70     else//存在
 71     {
 72         //a、寻找插入点
 73         LRC *pb = head, *pf = head;
 74         while(pb->time < pi->time && pb->next != NULL)
 75         {
 76             pf = pb;
 77             pb = pb->next;
 78         }
 79 
 80         //b、插入点的判断
 81         if(pb->time >= pi->time)//头部 中部插入
 82         {
 83             if(pb == head)//头部之前插入
 84             {
 85                 pi->next = head;
 86                 head = pi;
 87                 return head;
 88             }
 89             else//中部插入
 90             {
 91                 pf->next = pi;
 92                 pi->next = pb;
 93                 return head;
 94             }
 95         }
 96         else//尾部插入
 97         {
 98             pb->next = pi;
 99             return head;
100         }
101     }
102     return head;
103 }
104 //处理歌词
105 LRC * lrc_handle(char *buf[128], LRC *head)
106 {
107     int i = 0;
108     //开头的歌手,编曲等字符串
109     char *song_infor[] = {"ti", "ar", "al", "by"};
110     
111     system("clear");
112     while(buf[i] != NULL)
113     {
114         char flag = 0;
115         sscanf(buf[i],"%*c%c", &flag);
116         if(flag >= '0' && flag <= '9')//除了前四行
117         {
118             char *str_lrc = buf[i], *str_time = buf[i];
119             //每遇到"["判断是否这句歌词出现的次数
120             while(*str_lrc == '[')
121                 str_lrc += 10;
122 
123            
124             while(*str_time == '[')
125             {
126                 LRC temp;
127                 int minute = 0, second = 0, ms = 0;
128                 //取出每个节点的时分秒
129                 sscanf(str_time, "[%d:%d.%d]", &minute, &second, &ms);
130                 //统一单位到ms
131                 temp.time = minute * 60 + second + ((ms > 50)?1:0);//ms用四舍五入
132                  //把歌词内容赋值到该次链表的lrc_sentence
133                 strcpy(temp.lrc_sentence, str_lrc);
134                 //循环把时间插入链表节点
135                 head = lrc_insert(head, temp);
136                 str_time += 10;
137             }
138         }
139 
140         else
141         {
142             //拆分前四行的,拿到歌曲名等
143             
144             char tmp[128]="";
145             sscanf(buf[i], "%*[^:]:%[^]]", tmp);
146             if(i==0)
147             {
148                 cusor_moveto(17,1);
149                 printf("%s\n", tmp);
150             }
151             else if(i==1)
152             {
153                 cusor_moveto(17,2);
154                 printf("%s\n", tmp);
155             }
156             else if(i==2)
157             {
158                 cusor_moveto(17,3);
159                 printf("%s\n", tmp);
160             }
161             else if(i==3)
162             {
163                 cusor_moveto(17,4);
164                 printf("%s\n", tmp);
165             }
166         }
167         i++;
168     }
169 
170     return head;
171 }
172 //歌词滚屏
173 int times = 0;
174 int screen = 0;
175 
176 LRC* lrc_print(LRC *head)
177 {
178     int time=0;
179     char buf1[128]="";
180     char buf2[128]="";
181     char buf3[128]="";
182     char buf4[128]="";
183     while(1)
184     {
185         //光标定位
186         cusor_moveto(20,6);
187         printf("%02d:%02d",time/60,time%60 );
188         fflush(stdout);
189 
190         LRC *ret = search_link(head, time);
191         if(ret != NULL)
192         {
193             //滚起来
194             strcpy(buf1,buf2);
195             strcpy(buf2,buf3);
196             strcpy(buf3,buf4);
197             strcpy(buf4, ret->lrc_sentence);
198             
199             cusor_moveto(15,8);
200             printf("%s                                       ", buf1);
201 
202             cusor_moveto(15,9);
203             printf("%s                                      ", buf2);
204 
205             cusor_moveto(15,10);
206             printf("%s                                           ", buf3);
207 
208             cusor_moveto(15,11);
209             set_fg_color(COLOR_RED);
210             printf("%s                                         ", buf4);//当前歌词
211             set_fg_color(COLOR_BLACK  );
212             fflush(stdout);
213         }
214         //滚屏显示歌词
215         //第一行
216         
217 
218         sleep(1);
219         time++;
220     }
221     
222 }
223 
224 LRC* search_link(LRC *head, int time)
225 {
226     //1、判断链表是否存在
227     if(head == NULL)//不存在
228     {
229         printf("link not found\n");
230         return NULL;
231     }
232     else//链表存在
233     {
234         LRC *pb = head;
235 
236         //逐个将节点中的name 和 name比较 如果不相等 pb=pb->next
237         while(pb->time != time && pb->next != NULL)
238             pb = pb->next;
239 
240         //判断是否找到
241         if(pb->time == time)//找到
242             return pb;
243         else//没找到
244             return NULL;
245     }
246 
247     return NULL;
248 }
249 
250 void mplayer_play(char * song_path)
251 {
252     pid_t pid;
253     pid=fork();
254     if(pid<0)
255     {
256         perror("fork");
257     }
258     else if(pid==0)
259     {
260         close(1);
261         close(2);
262         execlp("mplayer","mplayer","-slave","-quiet",song_path,NULL);
263         exit(0);
264     }
265     else;
266 }

lrc.h

 1 #ifndef __LRC_H__
 2 #define __LRC_H__
 3 
 4 #include <stdio.h>
 5 #include <stdlib.h>
 6 #include <string.h>
 7 
 8 typedef struct lrc
 9 {
10     int time;
11     char lrc_sentence[128];
12     
13     struct lrc *next;
14 }LRC;
15 //创造链表头
16 LRC *lrc_create_head(void);
17 //顺序插入
18 LRC * lrc_insert(LRC *head, LRC tmp);
19 //读取歌词
20 char *read_song_lrc(char *lrc_name);
21 //切割歌词
22 void lrc_strtok(char *lrc_condent);
23 //处理歌词
24 LRC * lrc_handle(char *buf[128], LRC *head);
25 //遍历歌词
26 LRC* lrc_print(LRC *head);
27 extern LRC* search_link(LRC *head, int time);
28 void mplayer_play(char * song_path);
29 
30 #endif

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

posted @ 2020-03-20 08:37  进击的小尧好程序员  阅读(483)  评论(0编辑  收藏  举报