C语言阶段项目-歌词同步-《简单爱.mp3》

项目整体分析

分析歌词文件,将时间+歌词 组成一个节点 ,插入有序链表中 、设计模拟时钟,如果模拟时钟的时间 和链表中节点的时间相匹配, 就打印该时间点的歌词。
在这里插入图片描述

项目演示

在这里插入图片描述

歌词解析项目流程

1、将歌词文件 一次性去取到 内存中

a、用fopen打开歌词文件 FILE *fp = fopen(“简单爱.lrc”, “r”);
b、使用fseek将文件流指针 定位到文件尾部 获得文件总大小
c、使用rewind 复位文件流置针
d、根据文件总大小 从堆区 申请 合适的空间 char *arr
e、使用fread 读取文件数据 到内存中 arr
f、关闭文件

2、将arr指向的内存数据 按行"\r\n"切割 存入 字符指针数组 char *buf[128]={NULL};
已将歌词的每一行 存放 在 指针数组中 注意 记得保存 切割到的行数

3、逐行分析 buf[0]代表第0行 buf[n] 代表第n行
a、单独分析前4行
b、逐行分析剩下的行 将时间+歌词 一一对应 插入链表
建议:此处遍历一下链表

4、启动maplayer
5、模拟时钟
6、滚屏(5行)
//光标定位:(注意)
在这里插入图片描述
7、反显 当前歌词为 红色 其他歌词为黑色

总结

在做之前还怀疑自己能不能做出来,可做的过程中按照流程、步骤,尽管编写代码的过程中总会出现BUG,方法永远比问题多,比如这样提取带空格的字符串,以及调用函数、传参、返回值的类型匹配问题,这样一路坎坷的过程,也是给学习和进步空间 创造了一个宝贵的机会,后面我还想再解析多首歌曲,由于时间的关系,就只添加了进度条。加油!!!

下面是整个项目的代码
为了编译方便,这里创建了一个makefile,这样就不用每次都gcc一堆 .C文件,只需要make,敲回车,就把包含所有的.c文件编译了,
./main运行程序。
这样也方便调试结果。

makefile

exec=main
cc=gcc
obj=main.o lrc.o start_mplayer.o console.o #你要修改的地方
cflags=-Wall -g
$(exec):$(obj)
		$(cc) $^ -o $@ $(cflags)
%.o:%.c
		$(cc) -c $< -o $@ $(cflags)
clean:
		rm $(exec) *.o

main.c

/* ************************************************************************
 *       Filename:  main.c
 *    Description:  
 *        Version:  1.0
 *        Created:  2020年03月09日 20时00分02秒
 *       Revision:  none
 *       Compiler:  gcc
 *         Author:  YOUR NAME (), 
 *        Company:  
 * ************************************************************************/


#include <stdio.h>
#include "lrc.h"
#include<string.h>
#include"start_mplayer.h"
#include <stdlib.h>

int main(int argc, char *argv[])
{
	char *arr=NULL;
	LRC *head=NULL;
	int i=0;
	arr=get_lrc();//1、将歌词文件 一次性去取到内存中
	char *buf[128]={NULL};//2、将arr指向的内存数据 按行"\r\n"切割
	buf[i]=strtok(arr,"\r\n");
	while(buf[i]!=NULL)
	{
		i++;
		buf[i]=strtok(NULL,"\r\n");
	}
	int rows=i;//29行  保存行数
	head = sscanf_lrc(buf,rows,head);//3、逐行分析 buf[0]代表第0行  buf[n]代表第n行
	head = insert_4(buf,head);//链表插入前四行
	//print_link(head);//遍历链表
	mplayer_play("../music/简单爱.mp3");//4、启动mplayer
	sim_clok(head);//5、模拟时钟并打印歌词
	return 0;
}

lrc.c

/* ************************************************************************
 *       Filename:  lrc.c
 *    Description:  
 *        Version:  1.0
 *        Created:  2020年03月09日 20时01分14秒
 *       Revision:  none
 *       Compiler:  gcc
 *         Author:  YOUR NAME (), 
 *        Company:  
 * ************************************************************************/


#include <stdio.h>
#include "lrc.h"
#include <stdlib.h>
#include<string.h>
#include"console.h"
#include<unistd.h>

char *get_lrc(void)
{
	//a、用fopen打开歌词文件 FILE *fp = fopen("简单爱.lrc", "r");
	FILE *fp = fopen("简单爱.lrc","r");
	//b、使用fseek将文件流指针 定位到文件尾部 获得文件总大小
	fseek(fp,0,2);
	long len=0;
	len=ftell(fp);
	//c、使用rewind 复位文件流置针 
	rewind(fp);
	//d、根据文件总大小 从堆区  申请 合适的空间  char *arr 
	char *arr=(char *)calloc(1,len+1);
	if(arr==NULL)
	{
		perror("calloc");
		return NULL;
	}
	//e、使用fread 读取文件数据 到内存中 arr
	fread(arr,len,1,fp);
	fclose(fp);
	return arr;
}
LRC * sscanf_lrc(char *buf[],int rows,LRC *head)
{
	int i=4;
	//b、逐行分析剩下的行 将时间+歌词 一一对应 插入链表
	for(i=4;i<rows;i++)
	{
		LRC tmp;
		//提取歌词
		char *str_lrc=buf[i];
		while(*str_lrc == '[')
			str_lrc+=10;
		char lrc[128]="";
		sscanf(str_lrc,"%[^\n]",lrc);
		strcpy(tmp.lrc,lrc);//将提取到的歌词拷贝到、tmp.lrc
		
		//提取时间
		char *str_time=buf[i];
		while(*str_time == '[')
		{
			int min = 0,sec = 0,t = 0;
			sscanf(str_time,"[%d:%d.%d]",&min,&sec,&t);
			int time = min*60+sec;
			if(t>=50)
				time +=1;
			else
				time = time;
			tmp.time = time;//提取到的时间放入tmp.time
			head = insert_link(head,tmp);//调用链表的有序插入函数
			str_time += 10;
		}
	}
	return head;
}
LRC * insert_4(char *buf[],LRC *head)
{
	//a、单独分析前4行
	int i=0;
	char lrc_4[4][12]={""};
	for(i=0;i<4;i++)
	{
		sscanf(buf[i],"%*[^:]:%[^]]",lrc_4[i]);
	}
	char name[4][16]={"歌名:","歌手:","专辑:","制作:"};
	char tmp1[4][32]={""};
	for(i=0;i<4;i++)
	{
		sprintf(tmp1[i],"%s%s",name[i],lrc_4[i]);
	}
	for(i=3;i>=0;i--)
	{
		LRC tmp;
		strcpy(tmp.lrc,tmp1[i]);
		LRC *pi = (LRC *)calloc(1, sizeof(LRC));
		if (pi == NULL)
		{
			perror("calloc");
			return head;
		}
		*pi = tmp;
		pi->next = NULL;
		if (head == NULL)
		{
			head = pi;
		}
		else
		{
			pi->next = head;
			head = pi;
		}
	}
	return head;
}
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
	{
		LRC *pb = head, *pf = head;
		while((pb->time < pi->time) && (pb->next != NULL))
		{
			pf = pb;
			pb = pb->next;
		}
		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;
}
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;
}

void sim_clok(LRC *head)
{
	int time=0;
	clear_screen();
	while(1)
	{
		 cusor_moveto(12, 3);
		 set_fg_color(COLOR_BLACK );
		 printf("%02d : %02d",time/60,time%60);
         fflush(stdout);
		 print_lrc_with_music(head,time-1);//打印歌词
		 progres_bar(head,time);//打印进度条
         sleep(1);
         time++;
	}
	return ;
}

void print_lrc_with_music(LRC *head,int time)
{
	LRC *pb=head ;
	int row=0;
	char buf1[128]="";
	char buf2[128]="";
	char buf3[128]="";
	char buf4[128]="";
	char buf5[128]="";
	char buf6[128]="";
	char buf7[128]="";
	while(row<4)
	{
		cusor_moveto(30,2+row);
		set_fg_color(COLOR_BLUE);
		printf("%s\n",pb->lrc);
		pb=pb->next;
		row++;
	}	
	while (pb->time != time && pb->next->next->next->next->next->next->next != NULL)
		pb = pb->next;
	
	if (pb->time == time)
	{
		strcpy(buf1,pb->lrc);
		strcpy(buf2,pb->next->lrc);
		strcpy(buf3,pb->next->next->lrc);
		strcpy(buf4,pb->next->next->next->lrc);
		strcpy(buf5,pb->next->next->next->next->lrc);
		strcpy(buf6,pb->next->next->next->next->next->lrc);
		strcpy(buf7,pb->next->next->next->next->next->next->lrc);
		set_fg_color(COLOR_RED);
		cusor_moveto(15,9);
		printf("%s          ",buf1);
		set_fg_color(COLOR_BLACK);
		cusor_moveto(15,10);
		printf("%s          ",buf2);
		cusor_moveto(15,11);
		printf("%s          ",buf3);
		cusor_moveto(15,12);
		printf("%s          ",buf4);
		cusor_moveto(15,13);
		printf("%s          ",buf5);
		cusor_moveto(15,14);
		printf("%s          ",buf6);
		cusor_moveto(15,15);
		printf("%s          ",buf7);
		fflush(stdout);
	}
	return ;
}
int s=0;
void progres_bar(LRC *head,int time)
{
	LRC *pb=head;
	while(pb->next!=NULL)
		pb=pb->next;
	int time1 = pb->time+20;
	cusor_moveto(5,20);
	set_fg_color(COLOR_YELLOW );
	printf("%02d : %02d|",time/60,time%60);
	cusor_moveto(60,20);
	set_fg_color(COLOR_YELLOW );
	printf("|%02d : %02d",time1/60,time1%60);
	cusor_moveto(13,20);
	set_fg_color(COLOR_GREEN);
	printf(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");//47
	int t=s-1;
	cusor_moveto(13+t,20);
	if(time==time1/45*s) 
	{
		cusor_moveto(13+s,20);
		s+=1;
	}
}

lrc.h

/* ************************************************************************
 *       Filename:  lrc.h
 *    Description:  
 *        Version:  1.0
 *        Created:  2020年03月09日 20时02分25秒
 *       Revision:  none
 *       Compiler:  gcc
 *         Author:  YOUR NAME (), 
 *        Company:  
 * ************************************************************************/

#ifndef __LRC_H__
#define __LRC_H__
typedef struct lrc
{
	int time;
	char lrc[128];
	struct lrc *next;
}LRC;
extern char *get_lrc(void);
extern LRC * sscanf_lrc(char *buf[],int rows,LRC *head);
extern LRC * insert_link(LRC *head, LRC tmp);
extern LRC * insert_4(char *buf[],LRC *head);
extern void print_link(LRC *head);
extern void sim_clok(LRC *head);
extern void print_lrc_with_music(LRC *head,int time);
extern void progres_bar(LRC *head,int time);

#endif

下面的是光标定位函数,和字体颜色设置还有启动mplayer 的函数
start_mplayer.c

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
//启动mplayer播放器 
//参数song_path 为歌曲的路径
void mplayer_play(char * song_path)
{
	pid_t pid;
	pid=fork();
	if(pid<0)
	{
		perror("fork");
	}
	else if(pid==0)
	{
		close(1);
		close(2);
		execlp("mplayer","mplayer","-slave","-quiet",song_path,NULL);
		exit(0);
	}
	else
		;
}

start_mplayer.h

#ifndef __START_MPLAYER_H__
#define __START_MPLAYER_H__
//启动mplayer播放器 
//参数song_path 为歌曲的路径
extern void mplayer_play(char * song_path);
#endif

console.c

#include <stdio.h>
#include <stdlib.h>
#include "console.h"

void cusor_moveto(int x, int y)
{// ESC[y;xH
    printf("\033[%d;%dH",y,x);
    fflush(stdout);
} 

//保存光标位置
void cusor_get_pos(void)
{// ESC[s
    printf("\033[s");
    fflush(stdout);
} 

//恢复光标位置
void cusor_set_pos(void)
{// ESC[u
    printf("\033[u");
    fflush(stdout);
} 
//隐藏光标
void cusor_hide(void)
{
	printf("\033[?25l");
}
//显示光标
void cusor_show(void)
{
	printf("\33[?25h");
}
//清屏
void clear_screen(void)
{// ESC[2J
    printf("\033[2J");
    fflush(stdout);
}

/*
COLOR_RED              红
COLOR_BLACK            黑
COLOR_GREEN            绿
COLOR_BLUE             蓝
COLOR_YELLOW           黄
COLOR_WHITE            白
COLOR_CYAN             青
COLOR_MAGENTA          洋红
*/
//设置前景颜色
void set_fg_color(int color)
{// ESC[#m
    printf("\033[%dm",color);
    fflush(stdout);
}

//设置背景颜色
void set_bg_color(int color)
{// ESC[#m
    printf("\033[%dm",(color+10));
    fflush(stdout);
}

console.h

#ifndef  _CONSOLE_H_
#define  _CONSOLE_H_

#define     COLOR_RED              31
#define     COLOR_BLACK            30
#define     COLOR_GREEN            32
#define     COLOR_BLUE             34
#define     COLOR_YELLOW           33
#define     COLOR_WHITE            37
#define     COLOR_CYAN             36
#define     COLOR_MAGENTA          35
/*
COLOR_RED              红
COLOR_BLACK            黑
COLOR_GREEN            绿
COLOR_BLUE             蓝
COLOR_YELLOW           黄
COLOR_WHITE            白
COLOR_CYAN             青
COLOR_MAGENTA          洋红
*/

extern void cusor_moveto(int x, int y);//光标跳转到 y行 x列
extern void cusor_get_pos(void);//保存光标位置
extern void cusor_hide(void);//隐藏光标
extern void cusor_show(void);//显示光标
extern void cusor_set_pos(void);//恢复光标位置
extern void clear_screen(void);//清屏
extern void set_fg_color(int color);//设置字体前景色
extern void set_bg_color(int color);//设置字体背景色

#endif	//_CONSOLE_H_



发布了15 篇原创文章 · 获赞 11 · 访问量 2888

猜你喜欢

转载自blog.csdn.net/Evan_work/article/details/104850232
mp3