2 Star 0 Fork 1

sunnysab/Qt Lyric Operate

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
QLyric.cpp 18.85 KB
一键复制 编辑 原始数据 按行查看 历史
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709
/********************************************
* Class QLyric
* Qt Lyric 歌词解析类 类实现
*
* QLyric.cpp
* By sunnysab ([email protected])
* 不得用于收费软件,请在软件中标明使用本库及作者
*
*********************************************/
#include "QLyric.h"
#include <QFile> // Qt 文件操作, 歌词载入和保存依赖此项
#include <QString> // Qt 内置字符串操作
#include <QByteArray> // Qt 字节操作
#include <QTextStream> // Qt 文本操作类
// LRC_DEBUG 控制了类中一些 qDebug() 语句及某些函数
// 定义位于 QLyric.h 中
#ifdef LRC_DEBUG
#include <QtDebug> // Qt 调试功能类
#endif
/*
* 名 称: QLyric
* 属 性: 构造函数
* 功 能:
* 参 数:
*/
QLyric::QLyric()
{
RemoveAll();
}
/*
* 运 算: +
* 功 能:
* 说 明:
*/
QLyric QLyric::operator +(const QLyric &lrc2)
{
m_LineList += lrc2.m_LineList;
Sort();
return *this;
}
/*
* 运 算: +=
* 功 能:
* 说 明:
*/
QLyric QLyric::operator +=(const QLyric &lrc2)
{
m_LineList += lrc2.m_LineList;
Sort();
return *this;
}
QLyric QLyric::operator +=(const LYRICLINE &stLyricLine)
{
AppendLine(stLyricLine);
return *this;
}
/*
* 名 称: CompareTime
* 属 性: private
* 功 能: 返回 stLine1 的时间是否在 stLine2 之前
* 参 数: const LYRICLINE &stLine1 要比较的时间1
* const LYRICLINE &stLine2 要比较的时间2
* 说 明: 本函数提供给 sort() 使用的迭代器,见 QLyric::Sort()
*/
bool QLyric::CompareTime(const LYRICLINE &stLine1, const LYRICLINE &stLine2)
{
return stLine1.nTime < stLine2.nTime;
}
/*
* 名 称: GetTimeFromString
* 属 性: public & static
* 功 能: 将时间标签转换为以毫秒为单位的数值
* 参 数: const QString &strTime 要转换的时间标签
*/
LyricTime QLyric::GetTimeFromString(const QString &strTime)
{
LyricTime nXX = 0; // 百分之一秒
LyricTime nSS = 0; // 秒数
LyricTime nMM = 0; // 分钟数
int nIndex = 0; // 标志转换的起始位置
#ifdef LRC_DEBUG
qDebug() << "strTime: " << strTime << endl;
#endif
// 处理方式
// [MM:SS.XX] 标签, 将'['作为MM开始标志,以此类推
if ((nIndex = strTime.indexOf("[")) != -1)
{
// 这里的代码写长了,不过也就这个笨办法了
// 笔记:QString::toInt遇到非字符会返回0
nMM = strTime.mid(nIndex + 1, strTime.indexOf(":") - nIndex - 1).toInt();
}
if ((nIndex = strTime.indexOf(":")) != -1)
{
nSS = strTime.mid(nIndex + 1, strTime.indexOf(".") - nIndex - 1).toInt();
}
if ((nIndex = strTime.indexOf(".")) != -1)
{
nXX = strTime.mid(nIndex + 1, strTime.indexOf("]") - nIndex - 1).toInt();
}
return nMM * 60 * 100 + nSS * 100 + nXX;
}
/*
* 名 称: GetLyricFromString
* 属 性: public & static
* 功 能: 从行中提取出歌词内容
* 参 数: const QString &strTime 要提取的lrc行
* QString &strLyricString 存放提取出的歌词
* 返 回: LRC_NO_DATA 表示找不到时间标签,成功返回 LRC_SUCCESS
*/
LRC_RESULT QLyric::GetLyricFromString(const QString &strLine, QString &strLyric)
{
int nIndex = 0;
// 找到最后一个 ']' 的位置
if ((nIndex = strLine.lastIndexOf("]")) != -1)
{
/* 此时,
* strLine.size() - nIndex - 1
* 是歌词内容的长度
*/
strLyric = strLine.right(strLine.size() - nIndex - 1);
return LRC_SUCCESS;
}
return LRC_NO_DATA;
}
/*
* 名 称: AppendLine
* 属 性: public
* 功 能: 添加一行歌词
* 参 数: LYRICLINE &stLyricLine 要添加的行结构
*/
void QLyric::AppendLine(const LYRICLINE &stLyricLine)
{
m_LineList.append(stLyricLine);
return ;
}
/*
* 名 称: AppendLine
* 功 能: 添加一行歌词
* 属 性: public
* 参 数: unsigned int nTime 时间
* const QString &strNewLineData 歌词内容
*/
void QLyric::AppendLine(LyricTime nTime, const QString &strNewLine)
{
LYRICLINE stLyricLine;
stLyricLine.nTime = nTime;
stLyricLine.strLineData = strNewLine;
AppendLine(stLyricLine);
return ;
}
/*
* 名 称: InsertLine
* 功 能: 在 nLine 处插入一个新行
* 属 性: public
* 参 数: LyricIndex nLine 行索引
* const LYRICLINE &stLyricLine 要插入的新行
*/
void QLyric::InsertLine(LyricIndex nLine, const LYRICLINE &stLyricLine)
{
m_LineList.insert(nLine, stLyricLine);
return ;
}
/*
* 名 称: InsertLine
* 功 能: 在 nLine 处插入一个新行
* 属 性: public
* 参 数: LyricIndex nLine 行索引
* unsigned int nTime 歌词时间
* const QString &strNewLineData 歌词内容
*/
void QLyric::InsertLine(LyricIndex nLine, LyricTime nTime,
const QString &strNewLine)
{
LYRICLINE stLyricLine;
stLyricLine.nTime = nTime;
stLyricLine.strLineData = strNewLine;
InsertLine(nLine, stLyricLine);
return ;
}
/*
* 名 称: DeleteLine
* 功 能: 删除行
* 属 性: public
* 参 数: LyricIndex nLine 要删除行的索引
* 返 回: 如果找不到索引为 nLine 的数据,返回 LRC_PARA_ERR
* 调用者可以先确认行索引是否正确
*/
LRC_RESULT QLyric::DeleteLine(LyricIndex nLine)
{
// 由于 removeAt 不检查参数正确性,
// 需要进行检验
if (nLine >= 0 && nLine < m_LineList.size())
{
m_LineList.removeAt(nLine);
return LRC_SUCCESS;
}
return LRC_PARA_ERR;
}
/*
* 名 称: AlterLine
* 功 能: 修改行
* 属 性: public
* 参 数: LyricIndex nLine 要修改行的索引
* LYRICLINE stLyricLineNew 新的行数据
* 返 回: 如果找不到索引为 nLine 的数据,返回 LRC_PARA_ERR
* 调用者可以先确认行索引是否正确
*/
LRC_RESULT QLyric::AlterLine(LyricIndex nLine, LYRICLINE stLyricLineNew)
{
// 检查索引
if (nLine >= 0 && nLine < m_LineList.size())
{
// 去掉行中的换行符
stLyricLineNew.strLineData.replace("\r", "");
stLyricLineNew.strLineData.replace("\n", "");
// 替换
m_LineList.replace(nLine, stLyricLineNew);
return LRC_SUCCESS;
}
return LRC_PARA_ERR;
}
/*
* 名 称: AlterLine
* 功 能: 修改行
* 属 性: public
* 参 数: LyricIndex nLine 要修改行的索引
* LyricTime nTimeNew 新的时间
* const QString &strNewData 新的歌词内容
* 返 回: 如果找不到索引为 nLine 的数据,返回 LRC_PARA_ERR
* 调用者可以先确认行索引是否正确
*/
LRC_RESULT QLyric::AlterLine(LyricIndex nLine, LyricTime nTimeNew,
const QString &strNewData)
{
LYRICLINE stLyricLineNew;
stLyricLineNew.nTime = nTimeNew;
stLyricLineNew.strLineData = strNewData;
return AlterLine(nLine, stLyricLineNew);
}
/*
* 名 称: GetLyricLine
* 功 能: 获得索引为 nLine 的行的内容
* 属 性: public
* 参 数: LyricIndex nLine 要获取的行的索引
* LYRICLINE &LyricLine 一个 LYRICLINE 的引用
* 返 回: 如果找不到索引为 nLine 的数据,返回 LRC_PARA_ERR
* 调用者可以先确认行索引是否正确
*/
LRC_RESULT QLyric::GetLyricLine(LyricIndex nLine, LYRICLINE &LyricLine)
{
if (nLine >= 0 && nLine < m_LineList.size())
{
LyricLine = m_LineList.at(nLine);
return LRC_SUCCESS;
}
return LRC_PARA_ERR;
}
/*
* 名 称: Sort
* 功 能: 排序歌词
* 属 性: public
*/
void QLyric::Sort()
{
// 想起第一版写的一个类,还用冒泡排序,感慨啊
qSort(m_LineList.begin(), m_LineList.end(), &CompareTime);
}
/*
* 名 称: SetTimeOffset
* 功 能: 为全部歌词设置时间偏移
* 属 性: public
* 参 数: int nTimeOffsetMS 10 毫秒为单位的时间偏移量
* 说 明: nTimeOffsetMS 正为后移,负为前移,函数内部会对移动范围进行检查
*/
void QLyric::SetTimeOffset(const int nTimeOffset10MS)
{
LYRICLINE stLine;
int i = 0;
if (nTimeOffset10MS == 0 || m_LineList.empty() != false)
{
return ;
}
// 以下代码的意思是,如果是向前偏移,则对整个列表进行
// 排序,确保第一个元素是最小的,并检查前移是否会导致
// 某些元素出现问题(LyricTime 类型是无符号的)
if (nTimeOffset10MS < 0)
{
Sort();
}
foreach (stLine, m_LineList)
{
if (nTimeOffset10MS < 0 && m_LineList.at(i).nTime < nTimeOffset10MS)
{
stLine.nTime = 0;
}
else
{
stLine.nTime += nTimeOffset10MS;
}
m_LineList.replace(i, stLine);
i++;
}
return ;
}
/*
* 名 称: GetStringFromTime
* 功 能: 将以 百分之一秒 为单位的时间数据转换成 [MM:SS:XX] 格式
* 或 [MM:SS] 的字符串,详见注释
* 属 性: public
* 参 数: LyricTime nTime 以 百分之一秒 为单位的时间
* QString &strTime 转换后的字符串数据
* bool bIsSimple 表示是否以简单形式
* 返 回: 转换后的字符串
* 说 明: 见 GetStringFromTime (2)
*/
QString QLyric::GetStringFromTime(LyricTime nTime, QString &strTime,
bool bIsSimple /*= false*/)
{
return GetStringFromTime(nTime / 6000, // 分
nTime % 6000 / 100, // 秒
nTime % 100, // 百分之一秒
strTime, // 转换后的字符串
bIsSimple // 标志.
);
}
/*
* 名 称: GetStringFromTime
* 功 能: 将以 百分之一秒 为单位的时间数据转换成 [MM:SS:XX] 格式
* 或 [MM:SS] 的字符串,详见注释
* 属 性: public & static
* 参 数: LyricTime nMM 以 分 为单位的时间
* LyricTime nSS 以 秒 为单位的时间
* LyricTime nXX 以 百分之一秒 为单位的时间
* QString &strTime 转换后的字符串数据
* bool bIsSimple 表示是否以简单形式
* 返 回: 转换后的字符串
* 说 明: 如果 bIsSimple 为 true, 那么当 nXX == 0 时,字符串将转换成
* [MM:SS] 形式,不过为了使 LRC 文件更加清晰,建议选false
*/
QString QLyric::GetStringFromTime(LyricTime nMM, LyricTime nSS,LyricTime nXX,
QString &strTime, bool bIsSimple /*= false*/)
{
// 这个函数写的可能有些乱,代码比较多但功能很单一
// QString 还不是很熟练
// 使用 QString::sprintf 可能使代码量减少,但是文档不推荐使用
// 并且不知道跨平台移植会不会出问题(尽管C标准库有sprintf)
strTime = "[";
strTime += QString("%1").arg(nMM).rightJustified(2, '0');
strTime += ":";
strTime += QString("%1").arg(nSS).rightJustified(2, '0');
// 如果不要求略写,且 nXX != 0
// 增加 nXX 段
if ((!bIsSimple) || (nXX != 0))
{
strTime += ".";
strTime += QString("%1").arg(nXX).rightJustified(2, '0');
}
strTime += "]";
return strTime;
}
/*
* 名 称: PrintAll
* 功 能: 输出 m_LineList 内所有行
* 属 性: public
* 说 明: 此函数仅 LRC_DEBUG 声明时可用,供调试使用
*/
#ifdef LRC_DEBUG
void QLyric::PrintAll()
{
LYRICLINE stEachLine;
qDebug() << "PrintAll()" << endl;
foreach (stEachLine, m_LineList)
{
qDebug() << " nTime = " << stEachLine.nTime << "strLineData = "
<< stEachLine.strLineData << endl;
}
}
#endif
/*
* 名 称: ResolveLine
* 功 能: 解析歌词行,并将其添加至列表
* 属 性: private
* 参 数: const QString &strLine 一行歌词数据
* 返 回: 类型 LRC_RESULT
* 说 明: 返回 LRC_FORMAT_ERR 表示 strLine 的时间标签格式有错
* LRC_NO_DATA 请检查是否含有时间标签, 成功返回 LRC_SUCCESS
* 如果某一个时间标签无法解析或错误,转换继续执行
*/
LRC_RESULT QLyric::ResolveLine(const QString &strLine)
{
// 初始值设置为 true, 这样当 strLine 无时间标签(即应返回
// LRC_NO_DARA 时)不会导致返回 LRC_FORMAT_ERR
bool bErr = false;
// 检测时间标签准确性
if (CheckTimeLable(strLine))
{
int nIndex = 0; // 查找 "[" 的起点
int nCutStart = 0, // 时间标签开始处(自起点之后"["的位置)
nCutEnd = 0; // 时间标签终止处("]")
QString strTime; // 截取到的时间标签
QString strLyric;
// 分解时间标签
while ((nCutStart = strLine.indexOf("[", nIndex)) != -1)
{
nCutEnd = strLine.indexOf("]", nIndex);
if (nCutStart < nCutEnd)
{
strTime = strLine.mid(nCutStart, nCutEnd - nCutStart + 1);
if (GetLyricFromString(strLine, strLyric) == LRC_SUCCESS)
{
AppendLine(GetTimeFromString(strTime), strLyric);
}
else
{
bErr = true;
}
}
nIndex = nCutEnd + 1;
}
if (bErr != true)
{
return LRC_FORMAT_ERR;
}
// 只要有一个标签,nIndex 至少为 1
// 故 nIndex == 0 返回无标签数据
if (0 == nIndex)
{
return LRC_NO_DATA;
}
}
return LRC_FORMAT_ERR;
}
/*
* 名 称: CheckTimeLable
* 功 能: 检查时间标签是否准确
* 属 性: private
* 参 数: QString strTimeLable 要检查的时间标签
* 返 回: bool 类型 表示该时间标签是否合法
* 说 明: 代码有待完善
*/
bool QLyric::CheckTimeLable(const QString &strTimeLabel)
{
int nCount = 0;
bool bReturnVal = true;
QChar chTimeLabel = '\0';
if (strTimeLabel.at(0) != QChar('['))
{
return false;
}
if (strTimeLabel.count('[') != strTimeLabel.count(':'))
{
return false;
}
foreach(chTimeLabel, strTimeLabel)
{
nCount += chTimeLabel == '[' ? 1 : 0;
nCount -= chTimeLabel == ']' ? 1 : 0;
if (nCount != 1 && nCount != 0)
{
bReturnVal = false;
break;
}
}
return bReturnVal;
}
/*
* 名 称: ResolveLines
* 功 能: 解析歌词文件
* 属 性: private
* 参 数: QStringList strLines lrc文件全部歌词行
* 说 明: 函数会过滤每一行的换行符,并得到歌词相关信息
*/
void QLyric::ResolveLines(QStringList strLines)
{
QString strLine;
// 过滤换行符
strLines.replaceInStrings("\r", "");
strLines.replaceInStrings("\n", "");
foreach (strLine, strLines)
{
ResolveLine(strLine);
}
// 不支持歌词信息标签
}
/*
* 名 称: LoadFromFile
* 功 能: 从文件加载歌词
* 属 性: public
* 参 数: QString strFileName 要加载的文件名
* 返 回: LRC_RESULT 类型
* LRC_FILE_ERR 表示无法打开文件或者文件大小为空
* 返回 LRC_SUCCESS 表示无错误发生
*/
LRC_RESULT QLyric::LoadFromFile(const QString &strFileName)
{
QFile LrcFile;
bool bApiRet = false;
LrcFile.setFileName(strFileName);
bApiRet = LrcFile.open(QIODevice::ReadOnly | QIODevice::Text);
if (false != bApiRet && 0 != LrcFile.size())
{
QString strLastRead;
QStringList Lines;
QTextStream LrcInput(&LrcFile);
// 确保文件指针指向文件头
LrcInput.seek(0);
while (false == (strLastRead = LrcInput.readLine()).isNull())
{
Lines += strLastRead;
#ifdef LRC_DEBUG
qDebug() << strLastRead << endl;
#endif
}
LrcFile.close();
m_LrcFileName = strFileName;
ResolveLines(Lines);
return LRC_SUCCESS;
}
return LRC_FILE_ERR;
}
/*
* 名 称: SaveToFile
* 功 能: 保存内存中存储的歌词到文件
* 属 性: public
* 参 数: const QString &strFileName 要保存的文件名
* bool IsAscii 是否保存为 ANSI 编码格式
* false 表示使用 UTF-8 编码
* 返 回: LRC_RESULT 类型
* LRC_FILE_ERR 表示无法保存文件
* 返回 LRC_SUCCESS 表示无错误发生
*/
LRC_RESULT QLyric::SaveToFile(const QString &strFileName, bool IsAscii /*=true*/)
{
bool bOpenFile = false;
QFile LrcFile;
LYRICLINE stLine;
LrcFile.setFileName((strFileName != "") ? strFileName : m_LrcFileName);
bOpenFile = LrcFile.open(QIODevice::WriteOnly);
if (false != bOpenFile)
{
QString strTime;
QString strLyricLine;
foreach (stLine, m_LineList)
{
strLyricLine = (GetStringFromTime(stLine.nTime, strTime)
+ stLine.strLineData + "\r\n");
if (IsAscii)
{
LrcFile.write(strLyricLine.toLocal8Bit());
}
else
{
LrcFile.write(strLyricLine.toUtf8());
}
}
LrcFile.close();
return LRC_FILE_ERR;
}
return LRC_SUCCESS;
}
/*
* 名 称: GetLineCount
* 属 性: public
* 功 能: 获得内存中歌词总数(/行)
* 返 回: unsigned int 类型
* 歌词行的总数
*/
unsigned int QLyric::GetLineCount()
{
return m_LineList.count();
}
/*
* 名 称: RemoveAll
* 属 性: public
* 功 能: 删除所有歌词
*/
void QLyric::RemoveAll()
{
m_LineList.clear();
}
// FILE_END
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/sunnysab/Qt-Lyric-Operate.git
[email protected]:sunnysab/Qt-Lyric-Operate.git
sunnysab
Qt-Lyric-Operate
Qt Lyric Operate
master

搜索帮助