/**************************************************************************/
//歌词解析器
//优点:使用双向链表显示上下行歌词、时间较正确、可解析样例外的lrc文件
//其它:系统资源的使用没有做进一步优化
//作者:david.q@sz 2012.7.30 2263537@qq.com
/**************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include <time.h>
#include "console.h"
#define ISDEBUG 0
typedef struct lrc
{
unsigned int ltime;
char lwords[200]; //? char * lwords?
struct lrc * last;
struct lrc * next;
}LRCS;
/**************************************************************************/
//函数功能:插入到链表
//参数:
// head: 歌词文件名
// lrc_text: 文件指针
// 返回值:歌词字符串在内存的首地址
/**************************************************************************/
LRCS * just_insert( LRCS * head, LRCS * pi )
{
LRCS * pf, * pb;
pf = pb = head;
if ( NULL == head ) //作为head
{
head = pi;
head ->last = NULL;
head ->next = NULL;
}
else
{
while( ( pb->ltime <= pi->ltime ) && ( NULL != pb->next ) ) //向后走步找到合适的存放位置
{
pf = pb;
pb = pb->next;
}
if( pb->ltime >= pi->ltime ) //执行插入
{
if ( pb == head ) //把pi插为head,head次之
{
head ->last = pi;//
pi ->next = head;
head = pi;
head ->last = NULL;//
}
else //作为普通节点,pi插在pf前
{
pf ->next = pi;
pi ->next = pb;
pi ->last = pf;//
pb ->last = pi;//
}//end if
}
else //作为tail节点, pi放在pb后
{
pb ->next = pi;
pi ->next = NULL;
pi ->last = pb;//
}//endif
}//endif
return head;
}
/**************************************************************************/
//函数功能:读入lrc文件到内存,取得字符串首地址
//参数:
// lrc_filename: 歌词文件名
// lrc_text: 文件指针
// 返回值:歌词字符串在内存的首地址
/**************************************************************************/
char * lrc_read( const char * lrc_filename )
{
FILE * lrc_f;
char * lrc_text = NULL;
unsigned long int lrc_length = 0;
//打开.lrc
lrc_f = fopen( lrc_filename, "rb" );
if ( NULL == lrc_f )
{
perror(lrc_filename); //* 可注释//
return NULL; //打开文件失败
}
//文件长度
fseek( lrc_f, 0 , SEEK_END);
lrc_length = ftell ( lrc_f );
if ( -1L == lrc_length )
{
return NULL; //没有长度
}
rewind( lrc_f );
//申请内存
lrc_text = ( char * ) malloc ( lrc_length + 1 );
memset( lrc_text, '\0', lrc_length + 1);
if ( NULL == lrc_text )
{
return NULL; //内存申请失败
}
//读取文件内容
fread( lrc_text, 1, lrc_length, lrc_f );
//关闭文件
fclose( lrc_f );
return lrc_text;
}
/**************************************************************************/
//函数功能:把mm:hh::ss时间格式,+补偿 + 四舍五入后转为整型秒数
//参数:
// lrc: 时间首地址
// offset: 补偿时间
// 返回值:整型秒数
/**************************************************************************/
int number( char *lrc, float offset )
{
return (int)(
(*(lrc+1)-48)* 600 + //分的十位数
(*(lrc+2)-48)* 60 + //分的个位数
(*(lrc+4)-48)* 10 + //秒的十位数
(*(lrc+5)-48) + //秒的个位数
(*(lrc+7)-48)* 0.1 + //毫秒的十位数
0.5 + //四舍五入
offset
); //时间补偿
}
/**************************************************************************/
//函数功能:释放内存
//参数:
// head: 歌词链表首地址
// 返回值:无
/**************************************************************************/
void free_mem( LRCS * head )
{
LRCS * pb, * pf;
pf = pb = head;
while ( pb != NULL )
{
pf = pb;
pb = pb->next;
free( pf );
}
head = NULL;
free( head );
}
/**************************************************************************/
//函数功能:获取unix时间戳
//参数:无
// 返回值:整型无符号时间
/**************************************************************************/
unsigned long int time_now( void )
{
return time(NULL);
}
/**************************************************************************/
//函数功能:切割字符串,并插入到链表
//参数:
// lrc_text: 文件指针
// head: 空的链表首地址
// 返回值:链表首地址
/**************************************************************************/
LRCS * splite_to_link( char * lrc_text, int * line )
{
int i; //行循环用
int j; //二维数组用
int k; //time[k]循环用
int l=0; //行数累加用
int time[10]; //算时间,存在一维数组里
LRCS * head = NULL; //链表头
LRCS * pi = NULL; //插入链表的结构体
char ** lrc;
char *str[200];
char offset_s[10];
float offset=0;
//分行
str[*line] = strtok( lrc_text, "\r\n" );
while ( NULL != ( str[++*line] = strtok( NULL, "\r\n" ) ) )
;
lrc = str;//二级指针
l = 0;//行数
for ( i = 0; i < *line; i++ )
{
j = 0;
//判断为字母
if ( ( lrc[i][1] >= 'a' ) && ( lrc[i][1] <= 'z' ) )
{
pi = ( LRCS * )malloc( sizeof(LRCS) );//内存申请
pi->ltime = 0;//时间
if( strstr( *(lrc+i), "ti:" ) != NULL )
{
strcpy( pi->lwords, "歌 名: " );
}
else if ( strstr( *(lrc+i), "ar:" ) != NULL )
{
strcpy( pi->lwords, "歌 手: " );
}
else if ( strstr( *(lrc+i), "al:" ) != NULL )
{
strcpy( pi->lwords, "专 辑: " );
}
else if ( strstr( *(lrc+i), "by:" ) != NULL )
{
strcpy( pi->lwords, "歌词人: " );
}
else if ( strstr( *(lrc+i), "offset:" ) != NULL )
{
strncpy( offset_s, *(lrc+i)+8, strlen( *(lrc+i)) - 9 );
offset = (float) atoi( offset_s ) / 1000;
free( pi );
continue;
}
else
{
//未知的标签,跳过
free( pi );
continue;
}
strncat( pi->lwords, *(lrc + i)+4, strlen( *(lrc+i)) - 5 );//歌词(句)
head = just_insert( head, pi );//插入链表
l ++;
}
//判断为时间
else if ( ( lrc[i][1] >= '0' ) && ( lrc[i][1] <= '9' ) )
{
while ( **(lrc+i) == '[' )
{
//时间处理
time[j] = number( *(lrc+i), offset );
//指针移动
*(lrc+i)=*(lrc+i)+10;
j++;
}//end_while
//加入链表
for ( k = 0; k < j; k++ )
{
pi = ( LRCS * )malloc( sizeof(LRCS) );//内存申请
pi->ltime = time[k];//时间
strcpy( pi->lwords, *(lrc + i) );//歌词(句)
head = just_insert( head, pi );//插入链表
l ++;
}//end_for
}//end_if
//system("Pause");
}//end_for
//歌词句数
*line = l;
//释放内存
free(lrc_text);
return head;
}
/**************************************************************************/
//函数功能:按位置打印时钟
//参数:
// x,y 坐标
// time 整数秒
// 返回值: 无
/**************************************************************************/
void print_clock( int x, int y, int time )
{
int minute =0 ,second = 0;
GoToXY( x, y+1 );
SetText_Color( 7 );
minute = time / 60;
second = time % 60;
printf( "Time: %02d:%02d\r", minute, second );
}
/**************************************************************************/
//函数功能:显示歌词头部信息
//参数:
// head 歌词链表首地址
// x,y 坐标
// 返回值:头部信息条数
/**************************************************************************/
int print_heads( LRCS * head, int x, int y )
{
LRCS * pb;
pb = head;
SetText_Color( 7 );
if ( NULL == head )
{
return 0;
}
while ( NULL != pb )
{
if ( pb->ltime == 0 )
{
GoToXY( x, y++ );
printf( "%s", pb->lwords );
}
pb = pb->next;
}
return y;
}
/**************************************************************************/
//函数功能:启动播放器
//参数:mp3_filename mp3文件名首地址
// 返回值:无
/**************************************************************************/
void start_music( char * mp3_filename )
{
ShellExecute( NULL, NULL, "TTPlayer.exe", mp3_filename, NULL ,1 ); //执行千千静听
}
/**************************************************************************/
//函数功能:查找链表是否有�
- 1
- 2
- 3
- 4
前往页