首 页 | 新 闻 | 文 档 | 代 码 | 工 具 | 论 坛

Mp4Tech 首页  >  文 档  >   应用开发
 
 

歌词引擎 UpLyric



关于

源代码, 使用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

 


联系我们
便携式多媒体技术中心
All Rights Reserved