歌词引擎 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   浏览次数:

相关文章

相关评论   发表评论