歌词引擎 UpLyric
可用于手机的MP3播放器,PMP的音频播放器中
关于
源代码, 使用Linux作为软件平台的手机或者PMP产品,可直接使用。
其他平台的版本,可向作者索阅。自行移植,也是相当的简单
这个歌词分析引擎 版本兼容性非常好,支持 多种分割符号,如回车,回车换行,换行,或者根本没有分割符
当时间到时,返回该行的歌词供显示,否则返回 NULL
歌词格式介绍
lyric.h
================================================
/**
* Text Lyrics Filter Engine head file
* Copyright (C) 2006 Gavin Shaw <gavin at upsdn.net>
*
*
* @version : 0.76
* @date : 2006-5-18
* @author : Gavin Shaw <gavin at upsdn.net>
*
* @revision: 0.77
* @date : 2006-7-5
* @author : Gavin Shaw <gavin at upsdn.net>
*/
#ifndef __LYRIC_H__
#define __LYRIC_H__
#include <stdio.h> //printf fopen
#include <stdlib.h> //malloc
#include <string.h> //strncpy
#include <ctype.h> //isdigit
#include <sys/stat.h>
#include <assert.h> //assert
#define MAX_TAG_LEN 16
typedef int bool;
const bool false = 0;
const bool true = 1;
typedef struct _LToken {
unsigned int time; ///< Play time in seconds
unsigned int line; ///< Lyric line
struct _LToken *next; ///< Pointer to Next Token
}
T_LyricToken;
typedef struct _LMeta {
char *title; ///< Music Name
char *artist; ///< Singer
char *album; ///< Album
int lag; ///< sync lag/inching
}
T_LyricMeta;
typedef struct _LData {
T_LyricToken *firstToken;
T_LyricToken *activeToken;
unsigned int Lines; ///<Total Lines
char **lyric; ///<the array of lyric string for line
T_LyricMeta MetaInfo;
}
T_LyricData;
/**
* @brief Get Lyric Content for tokens It will search all the
* lines for the time
*
* @param time the played seconds
* @param pLyricData Lyric Data Pointer
* @return char * Lyric String started at the moments
* NULL if early
* @see LYRIC_LoadFromTextFile
*/
char * LYRIC_GetLine(unsigned int time, T_LyricData *pLyricData);
/**
* @brief Load Lyric From Text File,and parse it
*
* @param [in] lyricFile, the lyric filepath to parse
* @param [in] addrLyricData, point to the pointer of the
* lyric data structure
* @return true if load and parse successfully,or false if fail
*/
bool LYRIC_LoadFromTextFile(char *lyricFile, T_LyricData **addrLyricData);
/**
* @brief Free Lyric Resource
*
* @param pLyricData Lyric Data Pointer
* @return NONE
* @see LYRIC_LoadFromTextFile
*/
void LYRIC_Free(T_LyricData *pLyricData);
#endif
================================================
lyric.c
================================================
/**
* Text Lyrics Filter Engine Source File
* Copyright (C) 2006 Gavin Shaw <gavin at upsdn.net>
*
*
* @version : 0.76
* @date : 2006-5-18
* @author : Gavin Shaw <gavin at upsdn.net>
*
* @revision: 0.77
* @date : 2006-7-5
* @author : Gavin Shaw <gavin at upsdn.net>
*
* Brief: Compatible to all text lyric file
* 1) timetag
* [mm:ss]
* [mm:ss:xx]
* [mm:ss:xxx]
* [mm:ss.xx]
* [mm:ss.xxx]
* 2) token delimit
* CR (0x0d)
* NL (0x0a)
* CR NL(0x0d 0x0a)
* NULL (ie. all lyric in a single line)
* 3) multi timetag in one line
* for example,
* [00:32][02:54]I love you,my godfather
*/
#include "lyric.h"
/**
* @brief Convert Time Tag to Timestamp
*
* @param [in] TimeTag
* @param [in/out] Timestamp
* @return true if the tag is timeTag and be support,
* otherwise return false
* @remark Support [mm:ss] [mm:ss.xx] [mm:ss.xxx] [mm:ss:xxx]
*/
static bool extractTimeStamp(const char *TimeTag, unsigned int count, unsigned int *TimeStamp)
{
char TagDup[MAX_TAG_LEN];
strncpy(TagDup, TimeTag, count);
if ( isdigit(TagDup[1])
&& (TagDup[3] == ':')
) {
TagDup[3] = '\0'; // [mm_ss]
TagDup[6] = '\0'; // [mm_ss_
*TimeStamp = atoi(&TagDup[1])*60 + atoi(&TagDup[4]);
return true;
} else {
return false;
}
}
/**
* @brief Extract Music Metainfo to Lyric Data Structure
*
* @param [in] strat, the meta info start offset in the lyric
* buffer
* @param [in] strSize, metainfo Size
* @param [in/out] meta info pointer in the lyric data
* @return None
*/
static void extractMetaInfo(const char *start, unsigned int strSize, T_LyricMeta *meta)
{
unsigned int metaSize;
metaSize = strSize-4;
if (metaSize == 0) //blank meta info
return;
if ( (start[1] == 'a') && (start[2] == 'r')) { // artist
meta->artist = malloc(metaSize);
assert(meta->artist);
strncpy(meta->artist, &start[4], metaSize);
return;
} else if ((start[1] == 't') && (start[2] == 'i')) { // title
meta->title = malloc(metaSize);
assert(meta->title);
strncpy(meta->title, &start[4], metaSize);
return;
} else if ((start[1] == 'a') && (start[2] == 'l')) { // album
meta->album = malloc(metaSize);
assert(meta->album);
strncpy(meta->album, &start[4], metaSize);
return;
} else if ((start[1] == 'o') && (start[2] == 'f')) { // offset
meta->lag = atoi(&start[8]);
return;
}
}
/**
* @brief Add Token to Lyric Data according time order
*
* @param [in] time, the token time
* @param [in] lyricData, Lyric Data Pointer
* @param [in] n, the token line index
* @return None
*/
static void addToken(unsigned int time, T_LyricData *lyricData, unsigned int n)
{
T_LyricToken *pTemp;
T_LyricToken *index;
//malloc the memory for the new token, and initialize the token
pTemp = malloc(sizeof(T_LyricToken));
assert(pTemp);
pTemp->time = time;
pTemp->line = n;
pTemp->next = NULL;
if (NULL != lyricData->firstToken) {
for (index = lyricData->firstToken; index->next != NULL; index = index->next) {
//insert the token if time earlier the current token
if ( pTemp->time < index->next->time) {
pTemp->next = index->next;
index->next = pTemp;
break;
}
}
//the new token is the last token, append to the token list
if (index->next == NULL) {
index->next = pTemp;
}
} else { //the lyric token list is empty
lyricData->firstToken = malloc(sizeof(T_LyricToken));
assert(lyricData->firstToken);
lyricData->firstToken->next = pTemp;
}
}
/**
* @brief Malloca line space, and copy line content ,so that you
* can add it to line list of the lyric data structure
*
* @param [in] start, the line content start offset in the lyric
* buffer
* @param [in] count, the line count/the line number
* @return char *, pointer to the new line contents
*/
static char * addLine(const char * start, unsigned int count)
{
char *pStr;
pStr = malloc(count+1);
assert(pStr);
strncpy(pStr, start, count);
return pStr;
}
/**
* @brief scan lyric buffer, prepare calculate lyric lines
*
* @param[in] lyricBuffer, to scan
* @return char **, pointer to the pointer of the char
* ie. string array
*/
static char ** createStrArray(char *const lyricBuffer)
{
char *pToken = lyricBuffer;
int countToken = 0;
char **strArray = NULL;
do {
countToken ++; //add token count
pToken ++;
pToken = strchr(pToken,']');
}while (pToken != NULL);
if (countToken >0) {
strArray = malloc(countToken*sizeof(char *));
assert(strArray);
}
return strArray;
}
/**
* @brief free line content resource
*
* @param [in] strArray, the string array, ie. pointer to the
* pointer of the character
* @param [in] n, size of the array
* @return NONE
*/
static void destroyStrArray(char ** strArray, unsigned int n)
{
unsigned int i;
for (i=0; i<n; i++) {
free(strArray[i]);
}
free(strArray);
}
/**
* @brief parse the lyric text buffer, delimit token using TOKEN
* TAG, so the engine is compatible to the various lyric
* formats.
*
* @param [in] lyricBuffer, to parse
* @param [in] pLyricData, pointer to lyric data structure
* @return true if parse successfully,or false
*/
static bool delimitByTag(char * lyricBuffer, T_LyricData *pLyricData)
{
unsigned int n = 0; //line count
unsigned int token_len;
//unsigned int tokenCount = 0;
unsigned int Time;
bool tagType = false;
bool retValue = true;
char *token_head ;
char *token_tail = lyricBuffer;
token_head = strchr(token_tail, '[');
while (token_head != NULL) { // '[' is found
token_tail = strchr(token_head, ']');
if (token_tail != NULL) { // ']' is found
tagType = extractTimeStamp(token_head, token_tail-token_head, &Time);
if (false == tagType ) { //Meta Tag
extractMetaInfo(token_head, token_tail-token_head, &pLyricData->MetaInfo);
} else { //Time Tag
addToken(Time, pLyricData, n);
//tokenCount ++;
}
} else { //have no end tag ']'
retValue = false; //the lyric is INVALID
break;
}
token_head = strchr(token_tail, '[');
if ( (true == tagType)
&& (token_head > (token_tail+1))
) {
//lyric contents
pLyricData->lyric[n] = addLine(token_tail+1, (unsigned int)(token_head-token_tail-1));
n ++;
}
}
if ((true == tagType)
&& (true == retValue)
&& (token_head == NULL)
) {
//the final time token
token_len = strlen(token_tail+1);
if (token_len > 0) {
pLyricData->lyric[n] = addLine(token_tail+1, token_len);
}
pLyricData->activeToken = pLyricData->firstToken->next;
pLyricData->Lines = n;
}
free(lyricBuffer);
return retValue;
}
/**
* @brief Load Lyric From Text File,and parse it
*
* @param [in] lyricFile, the lyric filepath to parse
* @param [in] addrLyricData, point to the pointer of the
* lyric data structure
* @return true if load and parse successfully,or
* false
*/
bool LYRIC_LoadFromTextFile(char * lyricFile, T_LyricData **addrLyricData)
{
struct stat stats;
FILE *file;
char *lyric_buf;
//free it firstly to avoid memory leak
if (NULL != *addrLyricData) {
LYRIC_Free(*addrLyricData);
}
//file is invalid
if ((stat(lyricFile, &stats) == -1) ||
((file = fopen(lyricFile, "r")) == NULL)
) {
return false;
}
//file size is invalid
if ((stats.st_size > 16*1024) || (stats.st_size <1)) {
printf("MP3::Lyric Size is wrong\n");
return false;
}
lyric_buf = malloc(stats.st_size+1);
assert(lyric_buf);
if (fread(lyric_buf, 1, stats.st_size, file) != stats.st_size) {
free(lyric_buf);
fclose(file);
return false;
}
fclose(file);
*(lyric_buf+stats.st_size) = '\0';
*addrLyricData = malloc(sizeof(T_LyricData));
assert(*addrLyricData);
(*addrLyricData)->firstToken = NULL;
(*addrLyricData)->activeToken = NULL;
(*addrLyricData)->lyric = createStrArray(lyric_buf);
return delimitByTag(lyric_buf, *addrLyricData);
}
/**
* @brief Get Lyric Content for tokens It will search all the
* lines for the time
*
* @param time the played seconds
* @param pLyricData Lyric Data Pointer
* @return char * Lyric String started at the moments
* NULL if early
* @see LYRIC_LoadFromTextFile
*/
char * LYRIC_GetLine(unsigned int time, T_LyricData *pLyricData)
{
T_LyricToken *curToken = pLyricData->activeToken;
while (NULL != curToken) {
if (time == curToken->time) {
return pLyricData->lyric[curToken->line];
} else if (time < curToken->time) {
pLyricData->activeToken = curToken;
return NULL;
} else {
curToken = curToken->next;
}
}
return NULL;
}
/**
* @brief Free Lyric Resource
*
* @param pLyricData Lyric Data Pointer
* @return NONE
* @see LYRIC_LoadFromTextFile
*/
void LYRIC_Free(T_LyricData *pLyricData)
{
if (NULL == pLyricData)
return;
//free meta string
free(pLyricData->MetaInfo.album);
free(pLyricData->MetaInfo.title);
free(pLyricData->MetaInfo.artist);
//free line contents
destroyStrArray(pLyricData->lyric, pLyricData->Lines);
//free token
while (NULL != pLyricData->firstToken) {
pLyricData->activeToken = pLyricData->firstToken;
pLyricData->firstToken = pLyricData->firstToken->next;
free(pLyricData->activeToken);
}
free(pLyricData);
}
/*******test******/
int main(int argc, char *argv[])
{
T_LyricData *pLyricData;
char * filename;
int i;
if (argc != 2) {
printf("Usage:%s lyric_file\n", argv[0]);
return 1;
}
filename = argv[1];
pLyricData = NULL;
if (true == LYRIC_LoadFromTextFile(filename, &pLyricData)) {
for ( i=0; i<360; i++) {
printf("line%d:%s\n",i,LYRIC_GetLine(i,pLyricData));
}
} else {
printf("parse lyric error\n");
}
return 0;
}
=================================================
作者:Gavin Shaw 更新日期:2006-07-06
来源:upsdn.net
浏览次数:
相关文章
相关评论 发表评论
- No Comments