基于LINUX的歌词解析系统(附程序)

实现功能:歌词对应,歌词滚屏显示,歌词总时长,多首歌曲选择播放,进度条(附加动画)

成果演示:在这里插入图片描述代码
主函数代码:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include"start_mplayer/start_mplayer.h"
#include"fun/fun.h"
#include "fun/console.h"

int main(int argc, char const *argv[])
{
    while (1)
    {
        int sim_time = 0;//模拟时钟时间
        unsigned long lrc_length = 0;//歌词总长度
        char *ret = NULL;//存放所有歌词的地址
        char *str[128]={NULL} ;//分行后的歌词地址数组
        int line = 0,rea_line = 0;//line为切割分的歌词行数,rea_line 为歌词总时长
        LRC *head = NULL;//链表头
        char cmd[16]="";//存放指令
        char song[32]="";//歌词名

        //提示并输入歌曲
        clear_screen();
        cusor_show();
        cusor_moveto(5,15);
        printf("歌库现存:简单爱  只因你太美   野狼  雾里看花 烤面筋  Lemon  大花轿\n");//自己添加歌曲
        cusor_moveto(5,16);
        printf("Please choose the song you want to listen to:\n ");
        cusor_moveto(5,17);
        scanf("%s",song);
       
        //获取歌词
        ret = get_lrc(&lrc_length,ret,song);
        if (NULL == ret)
        {
            cusor_moveto(20,19);
            printf("该歌曲尚未收录!!!\n");
            cusor_moveto(20,20);
            printf("拜拜了宝贝T.T\n");
            sleep(2);
            cusor_moveto(20,20);
            printf("真的要退出了呢  \n");
            sleep(2);
            cusor_moveto(20,20);
            printf("退出成功!!!T.T   \n");
            sleep(2);
            clear_screen();
            cusor_show();
            return 0;
            
        }
        
        //printf("lrc_length=%lu,%p\n",lrc_length,ret);

        //歌词分行
        line = cut_lrc(ret,str);
        //printf("%d",line);

        //歌词存放
        head = save_lrc(head,str,line);
        rea_line = print_link(head);//获取歌词时长
        //printf("%d",rea_line);

        //播放歌曲
        mplayer_play(song);

        //启动模拟时钟,进行输出
        sim_clock(head,str,sim_time,rea_line);

        //放完一首歌,进行人机交互
        cusor_moveto(20,23);
        printf("Do you want to keep listening?yes or no\n");
        cusor_moveto(20,24);
        scanf("%s",cmd);
        if (strcmp(cmd,"no")==0)
        {
            break;
        }
        else if (strcmp(cmd,"yes")==0)
        {
            continue;
        }
        else
        {
            printf("I don't know,I'm going to default to yes!\n");
            
        }
        
        free(*str);
    }
       
    return 0;
}

功能函数:
包括获取歌词,将歌词分行,每句歌词存进链表节点,模拟时钟,查找对应时间的歌词

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

因为这里是将歌词放在了一个子文件夹song里,所以获取歌词时候要注意文件地址

/**********************获取歌词******************/
char * get_lrc(unsigned long *length,char *data,char *path)
{ 
    char buf[32]="";
    sprintf(buf,"./song/%s.lrc",path);

    FILE *fp = NULL;
    fp = fopen(buf,"r");
    if (NULL == fp)       
        return NULL;
        
    sscanf(buf,"%[^lrc]",path);
     
    /**********************获取文件长度******************/
    fseek(fp,0,2);//流指针指到末尾
    *length = ftell(fp);//获取长度
    rewind(fp);//指针复位

    //申请堆区空间
    data = (char *)calloc(1,*length);
    if (NULL == data)
    {  
        perror("calloc");
        return NULL ;
    }

    //一次性读取内容
    fread(data,*length,1,fp);

    //关闭文件
   fclose(fp);

   return data;
    
}

strtok函数切割歌词

/**********************歌词分行******************/

int cut_lrc(char *buf,char **str)
{  
  
    int i = 0;
  
    //第一次切割
    str[i] = strtok(buf, "\r\n");

	//第2~n切割
	while(str[i] != NULL)//保证上一次切割正常 才有进行下一次切割的必要
	{
		i++;
		str[i] = strtok(NULL,"\r\n");
    //while(str[i++] = strtok(str[i],"\r\n"));
    }
 
    return i; 
  
}

/**********************歌词存放******************/
/**********************前四行*******************/
void save_lrc_four(char **str)
{
    
    int i = 0;
    char tmp[32]="",name[32]="";
    for(i=0;i<4;i++)
   {
        sscanf(str[i],"[%[^:]:%[^]]",tmp,name);
       if (strcmp(tmp,"ti") == 0)
       {
            set_fg_color(COLOR_BLUE);
            cusor_moveto(45, 2);
            printf("歌名:%s\n",name);
       }
       else if (strcmp(tmp,"ar") == 0)
       {
            cusor_moveto(45, 3);
            printf("歌手:%s\n",name);
       }
        else if (strcmp(tmp,"al") == 0)
       {
            cusor_moveto(45, 4);
            printf("专辑:%s\n",name);
       }
        else if (strcmp(tmp,"by") == 0)
       {
            cusor_moveto(45, 5);
            printf("来自:%s\n",name);
       }
   }
   return;
}
/**********************歌词内容存放******************/

LRC * save_lrc(LRC *head,char **str,int n)
{
    LRC pb;
    int i = 0;

   //歌词输出
    for ( i = 4; i < n; i++)
    {
        char *str_lrc = str[i];

        //指向歌词位置
        while('[' == *str_lrc)    
            str_lrc += 10; 

        //时间用s为单位
        char *str_time = str[i];

        while ('[' == *str_time)
        {
            int min = 0,sec = 0,time = 0;
            sscanf(str_time,"[%d:%d:%*d]",&min,&sec);
            time = min*60+sec;
            //printf("%d\n",time);
            
            //时间,歌词一一对应插入链表节点
            pb.time = time;
            strcpy(pb.lrc,str_lrc);

            //有序插入
            head = insert_link(head,pb);

            str_time += 10;

        }
    
    }
    
  
    return head;
}


有序插入的方法将歌词放入链表储存


/**********************有序插入******************/
LRC * insert_link(LRC *head,LRC pb)
{
    LRC *pi = (LRC *)calloc(1,sizeof(LRC));
    if (NULL ==pi)
    {
        perror("calloc");
        return head;
    }

    //pb赋值给pi
    *pi = pb;
    pi->next = NULL;
    
    if (NULL == head)
    {
        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;
		}
        
    }
      
}

查找与时间对应的歌词

/**********************查找节点******************/
LRC* search_link(LRC *head, int time)
{
	//1、判断链表是否存在
	if(head == NULL)//不存在
	{
		printf("link not found\n");
		return NULL;
	}
	else//链表存在
	{
		LRC *pb = head;
        
		//逐个将节点中的time 和 time比较 如果不相等 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 sim_clock(LRC *head,char **str,int time,int end)
{
    LRC *ret = NULL;
    char buf[12][128]={""};
    int n=5, x, y;
    char prb[31]="******************************";//30个*
    int s = 0;
    
    clear_screen();
    cusor_moveto(0,5); 
    save_lrc_four(str);
     
    while(1)
    {
        /**********************大风车啊吱悠悠的转******************/
        if (time %2 == 0)
        {    
            cusor_moveto(0,3);
            for (y = 2 * n + 1; y >= -2 * n - 1; y--)
            {
                for (x = -2 * n - 1; x <= 2 * n + 1; x++)
                {
                    if (x == 0 && y == 0)putchar(' ');
                    else if (abs(x) + abs(y) <= 2 * n + 1 && (x==0||y==0||(double)y/x>fabs((double)y/x)/((double)y/x)))putchar('*');
                    else putchar(' ');
                }
                putchar('\n');
            }
        }
        else
        {
            cusor_moveto(0,3);
            for (x = 2 * n + 1; x >= -2 * n - 1; x--)
            {
                for (y = -2 * n - 1; y <= 2 * n + 1; y++)
                {
                    if (y == 0 && x == 0)putchar(' ');
                    else if (abs(y) + abs(x) <= 2 * n + 1 && (x==0||y==0||(double)y/x>fabs((double)y/x)/((double)y/x)))putchar('*');
                    else putchar(' ');
                }
                putchar('\n');
            }
        }
        
        save_lrc_four(str);//输出四个歌头
        cusor_hide();//隐藏光标

        /**********************时间******************/
        cusor_moveto(29,20);//移动到20行29列
        printf("%02d:%02d",time/60,time%60);

        /**********************进度条******************/
        s=time*sizeof(prb)/end;//end歌曲总长度   
        prb[s]='>';//*变>
        printf("|%s |",prb);//进度条

        /**********************歌曲总时长******************/
        cusor_moveto(68,20);//移动到20行66列
        printf("%02d:%02d",end/60,end%60);
        fflush(stdout);
       
       /**********************查找时间对应歌词输出******************/
        ret = search_link(head,time);//查找歌词
        if (NULL != ret)
        {   
            int i =0;
            for(i=0;i<11;i++)
            {
                strcpy(buf[i],buf[i+1]);
            }
                strcpy(buf[11],ret->lrc);
          
           for ( i = 0; i < 11; i++)
           {
                cusor_moveto(36,7+i);
                printf("%s                 ",buf[i]);
           }
            cusor_moveto(36,18);
            set_fg_color(COLOR_RED);
            printf("%s                 ",buf[11]);
            set_fg_color(COLOR_BLUE);
            fflush(stdout);
            if (ret->next==NULL)
            {
                sleep(5);
                break;
            }

        }
            
        sleep(1);
        time++;
    
    }
    
    return;

}

得到歌词的最后一句对应的时间,看做最终时间,但是误差较大

/**********************遍历节点******************/
int print_link(LRC *head)
{
    int i = 0;
	if(head == NULL)//链表不存在
	{
		printf("link not find\n");
		return 0;
	}
	else
	{
		LRC *pb = head;
		while(pb != NULL)
		{
			i=pb->time;
			//pb指向下一个节点
			pb = pb->next;
		}
	
	}

	return i+5;
}

功能函数库

#ifndef __FUN_H__
#define __FUN_H__
typedef struct lrc
{
    int time;
    char lrc[128];

    struct lrc *next;
     
}LRC;


extern void sim_clock(LRC *head,char **str,int time,int end);//模拟时钟 参数time 为模拟时钟时间
extern char * get_lrc(unsigned long *length,char *data,char *path);//获取歌词 参数length 为文件长度 data 为文件内容 返回存文件字符串的空间地址
extern int cut_lrc(char *buf,char **str);//分割歌词 参数buf 为文件内容 str 为切割后的文件 
extern LRC * save_lrc(LRC *head,char **str,int n);//保存歌词
extern void save_lrc_four(char **str);//歌头内容
extern LRC * insert_link(LRC *head,LRC pb);//有序插入
extern LRC* search_link(LRC *head, int time);//查找链表
extern int print_link(LRC *head);//遍历链表
#endif

光标定位及颜色改变代码

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

对应的库函数

#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_



歌曲播放代码,因放在子文件夹中,所以makefi函数里需要标注地址

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include<string.h>
//启动mplayer播放器 
//参数song_path 为歌曲的路径
void mplayer_play(char * song_path)
{

	song_path = strcat(song_path,"mp3");
	
	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
		;
}

库函数

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

makefile函数

exec=main

cc=gcc

obj=main.o ./start_mplayer/start_mplayer.o  ./fun/fun.o  ./fun/console.o#你要修改的地方

cflags=-Wall -g

$(exec):$(obj)

	$(cc) $^ -o $@ $(cflags)

%.o:%.c

	$(cc) -c $< -o $@ $(cflags)

clean:

	rm $(exec) *.o

总结:麻雀虽小,五脏俱全,虽有不足,然心甚慰,道阻且长,不负年华。

附:程序链接
链接: https://pan.baidu.com/s/10ZVx-ADWE7RNVNL8ys6c3w?pwd=kavn 提取码: kavn 复制这段内容后打开百度网盘手机App,操作更方便哦

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值