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

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:大脸猫]

要得到的结果:

 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

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

Ubuntu下实现歌词解析的更多相关文章

  1. 解决Ubuntu下Apache不解析PHP问题

    这两天笔者遇到了一个很操蛋的问题——Apache无法解析PHP代码了,之前一直用的挺好的,突然就挂了,然后在网上疯狂的找解决办法,但是大都是php5的版本,而我却是7的版本,我就先顺便把5版本的解决方 ...

  2. ubuntu下解析udt数据包

    udt是通过udp进行端到端可靠传输的一个协议,有其默认拥塞控制算法. 之前ubuntu下wireshark的版本是1.10,不能直接解析udt数据包[1],升级到最新的2.0.0即可过滤udt数据包 ...

  3. Ubuntu下安装JDK以及相关配置

    1.查看系统位数,输入以下命令即可 getconf LONG_BIT 2.下载对应的JDK文件,我这里下载的是jdk-8u60-linux-64.tar.gz 3.创建目录作为JDK的安装目录,这里选 ...

  4. ubuntu下升级R版本

    ubuntu下升级R版本   在测试<机器学习 实用案例解析>一书的邮件分类代码时,windows系统下rstudio中无法读取特殊字符,在ubuntu下可以.在ubuntu虚拟机下安装t ...

  5. Ubuntu下的PHP开发环境架设

    Ubuntu下的PHP开发环境架设   今天重新装了ubuntu那么就吧过程记录下. 打开终端,也就是命令提示符. 我们先来最小化组建安装,按照自己的需求一步一步装其他扩展.命令提示符输入如下命令: ...

  6. ubuntu 下wireshark 软件安装与使用

    在ubuntu下,使用wireshark也是很有必要的.虽然可以使用tcpdump等工具. ubuntu:11.10     1. sudo apt-get install wireshark     ...

  7. ubuntu下配置hosts

    由于Chrome浏览器访问问题,需要配置hosts. 在Ubuntu系统下,需要修改/etc/hosts文件,修改完之后要重启网络.具体过程如下:1.修改hostssudo vi /etc/hosts ...

  8. [转载]--Ubuntu下修改DNS重启也能用的方法

    安装好Ubuntu之后设置了静态IP地址,再重启后就无法解析域名.想重新设置一下DNS,打开/etc/resolv.conf cat /etc/resolv.conf# Dynamic resolv. ...

  9. Ubuntu下sqlite3的安装及使用

    Sqlite是一款轻型的数据库,实现了多数SQL-92标准,包括事务(原子性,一致性,隔离性和持久性 ACID),触发器与多数复杂查询.对于一个移动手持设备的应用开发者,Sqlite是居家旅行必备数据 ...

随机推荐

  1. LeetCode Day 7

    LeetCode0012 罗马数字包含以下七种字符: I, V, X, L,C,D 和 M. 字符 I V X L C D M 数值 1 5 10 50 100 500 1000 例如, 罗马数字 2 ...

  2. QT .和::和:和->

    在学习C++的过程中我们经常会用到.和::和:和->,在此整理一下这些常用符号的区别.1.A.B则A为对象或者结构体2.A->B则A为指针,->是成员提取,A->B是提取A中的 ...

  3. 跨越真实和虚拟世界的边界——走近SIGGRAPH 2014大会

    2014大会" title="跨越真实和虚拟世界的边界--走近SIGGRAPH 2014大会"> 作者:孙鑫 微软亚洲研究院研究员 一场大会振奋一座城 温哥华位于加 ...

  4. 题解 HDU 3698 Let the light guide us Dp + 线段树优化

    http://acm.hdu.edu.cn/showproblem.php?pid=3698 Let the light guide us Time Limit: 5000/2000 MS (Java ...

  5. 格式化MyEclipse代码(java、jsp、js)行的长度@修改java代码字体@修改Properties文件编码方式

    每次用MyEclipse/Eclipse自带的快捷键Ctrl+shift+f格式化代码时,如果原来的一行代码大于80列,Eclipse就会自动换为多行.如果想格式化代码后不想让代码换行可以通过以下方式 ...

  6. Tozan and Gezan(x*y<a*b)

    E - Tozan and Gezan Time limit : 2sec / Memory limit : 256MB Score : 700 points Problem Statement Yo ...

  7. 4 Values whose Sum is 0 (二分+排序)

    题目: The SUM problem can be formulated as follows: given four lists A, B, C, D of integer values, com ...

  8. 重启aliyun esc 需要重新启动redis

    /export/sorftware ./redis-server ../redis.conf redis-server  配置路径 如redis-server  /etc/redis.conf 不是后 ...

  9. 广州CVTE招聘-测试开发工程师

    内推邮箱:keweisheng@cvte.com 地点:广州 公司简介 CVTE成立于2005年,总部位于广州科学城,旗下设有多家独立的子公司,在香港设有全球服务中心,在国内设有21个营销服务中心和近 ...

  10. SetTimeout()多次运行函数后越来越快的问题

    问题原因很简单,但是由于代码逻辑问题,一直没有考虑到: 网上有个帖子说的很明白:原帖入口 假如你在0时刻点击了一下按钮,那么500ms时数字会跳一下,1000ms会再跳一下,依次类推,1500,200 ...