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

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. node.js 和 npm/cnpm/nrm 的安装

    node.js 和 npm/cnpm/nrm 的安装 安装 node.js.去 官网 下载,下载 LTS 版本的.安装时一路点确定,不要改动任何设置. 在 git-bash 或是 cmd 下,输入 n ...

  2. jQuery 源码学习 - 02 - jQuery.fn.extend 与 jQuery.extend

    参考资料:[深入浅出jQuery]源码浅析--整体架构,备用地址:chokcoco/jQuery-. extend 方法在 jQuery 中是一个很重要的方法.jQuery 内部用它来拓展静态方法或者 ...

  3. Ionic3学习笔记(八)使iOS端、Android端 Navbar 透明化

    本文为原创文章,转载请标明出处 若要使iOS端 Navbar 透明,只需要给 ion-navbar 添加 transparent 属性,但是Android端依旧会有一条border在,所以再给 ion ...

  4. Pandas提取单元格的值

    如提取第1行,第2列的值: df.iloc[[0],[1]] 则会返回一个df,即有字段名和行号. 如果用values属性取值: df.iloc[[0],[1]].values 返回的值会是列表,而且 ...

  5. Python包管理工具setuptools相关

    setup函数常用参数: --name                         包名称 --version                      包版本 --author          ...

  6. zabbix监控mysql数据库信息脚本

    ---恢复内容开始--- 在/usr/local/zabbix/etc/zabbix_agentd.conf增加 # 获取mysql性能指标 UserParameter=mysql.status[*] ...

  7. Event Loop事件循环,GET!

    JS中比较让人头疼的问题之一要算异步事件了,比如我们经常要等后台返回数据后进行dom操作,又比如我们要设置一个定时器完成特定的要求.在这些同步与异步事件里,异步事件肯定是在同步事件之后的,但是异步事件 ...

  8. 量化投资学习笔记29——《Python机器学习应用》课程笔记03

    聚类的实际应用,图像分割. 利用图像的特征将图像分割为多个不相重叠的区域. 常用的方法有阈值分割,边缘分割,直方图法,特定理论(基于聚类,小波分析等). 实例:利用k-means聚类算法对图像像素点颜 ...

  9. 如何成为一名高级C++程序员

     C++这门语言从诞生到今天已经经历了将近30个年头.不可否认,它的学习难度都比其它语言较高.而它的学习难度,主要来自于它的复杂性.现在C++的使用范围比以前已经少了很多,java.C#.python ...

  10. Java并发编程(01):线程的创建方式,状态周期管理

    本文源码:GitHub·点这里 || GitEE·点这里 一.并发编程简介 1.基础概念 程序 与计算机系统操作有关的计算机程序.规程.规则,以及可能有的文件.文档及数据. 进程 进程是计算机中的程序 ...