基於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/1l53hZ7y-PMX06vZFuyxVXA
提取碼:kbnh
複製這段內容後打開百度網盤手機App,操作更方便哦

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章