酷狗KRC歌词文件解析和与LRC的转换
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using zlib;//using前要先引用
namespace KrcLib
{
publicclass KrcReader : IDisposable
{
private Stream _sourceStream = new MemoryStream();
public Stream SourceStream
{
get { return _sourceStream; }
privateset { _sourceStream = value; }
}
///
///用包含krc数据的流创建
///
///包含krc数据的流
public KrcReader(Stream sourceStream)
{
sourceStream.CopyTo(this.SourceStream);
this.SourceStream.Position = 0;
}
///
///打开krc文件
///
///文件全路径
public KrcReader(string fileName)
{
this.SourceStream = new FileStream(fileName, FileMode.Open, FileAccess.Read);
}
///
///将包含krc数据的流解析为包含明码的流
///
///
public Stream GetKrcStream()
{
//krc数据需要先解密再解压缩
//在这之前先去掉头部4字节的krc歌词版本号:krc1 其HEX为:6b 72 63 31
//解密:遍历字节时循环地使每个字节与数组h中对应元素异或运算
//解压缩:zlib使用LZ77的变种算法,DEFLATE的算法
char[] h = { '@', 'G', 'a', 'w', '^', '2', 't', 'G', 'Q', '6', '1', '-', 'Î', 'Ò', 'n', 'i' }; MemoryStream outStream = new MemoryStream();
ZOutputStream outZStream = new ZOutputStream(outStream);
SourceStream.Position = 4;//去掉头部4字节
int len = 0;
byte[] databuffer = newbyte[1024];
while ((len = SourceStream.Read(databuffer, 0, 1024)) > 0)
{
int m;
for (int k = 0; k < len; k++)
{
m = k % 16;
databuffer[k] = (byte)(databuffer[k] ^ h[m]);//异或解密
}
outZStream.Write(databuffer, 0, len);//zlib解压缩
}
outZStream.finish();
MemoryStream tmp = new MemoryStream();
outStream.Position = 0;
outStream.CopyTo(tmp);//复制新的流以便outZStream.Close()时不会关闭要返回的流
tmp.Position = 0;//tmp被CopyTo时到了尾部,返回前需要重置位置
outStream = tmp;//指向刚复制的流用于返回
outZStream.Close();
return outStream;
}
///
///将包含krc数据的流解析为字符串
///
///
publicstring GetKrcString()
{
using (Stream decodedStream = GetKrcStream())
{
byte[] buffer = newbyte[decodedStream.Length];
int i = decodedStream.Read(buffer, 0, buffer.Length);
return Encoding.UTF8.GetString(buffer, 0, i);
}
}
///
///将包含krc数据的流解析为按行存储的列表
///
///
public List
{
List
using (StreamReader sr = new StreamReader(GetKrcStream(), Encoding.UTF8))
{
while (!sr.EndOfStream)
rowList.Add(sr.ReadLine());
}
return rowList;
}
///
///将包含krc数据的流转换为按行存储的lrc格式列表
///
///
public IEnumerable
{
List
//List
Regex rowReg = new Regex(@"^\[\d+,\d+\](<\d+,\d+,\d+>.+)*");//匹配一行krc格式的歌词
Regex timeLableReg1 = new Regex(@"^\[\d+,\d+\]");//匹配krc格式行的时间
Regex timeLableReg2 = new Regex(@"<\d+,\d+,\d+>");//匹配krc格式字的时间
char[] splitChars = { '[', ',', ']' };//用于取得每行的开始时刻
Regex idLabelReg = new Regex(@"^\[(ar|ta|al|by|offset|encoding|la|fm|wl|wm|co|ad):.*\]$", RegexOptions.IgnoreCase);
IEnumerable
where idLabelReg.IsMatch(row) == true
select row;
IEnumerable
where rowReg.IsMatch(row) == true
let newRow2 = timeLableReg2.Replace(row, "")//去除对每个字的时间标签,如<0,751,0>
let timeKrcLabel = timeLableReg1.Match(newRow2).Value//获取对行的时间标签,如[2781,3355]
let timeKrc = Convert.ToInt32(timeKrcLabel.Split(splitChars, StringSplitOptions.RemoveEmptyEntries)[0]) * 10000//获取每行的开始时刻,以100纳秒为单位
let timeLrc = new DateTime(timeKrc).ToString("[mm:ss.ff]")//生成lrc格式的时间标签,如[00:02.78]
//let timeLrc = string.Format("[{0:mm}:{0:ss}.{0:ff}]", new DateTime(timeKrc))
//let timeKrc = Convert.ToSingle(timeKrcLabel.Split(splitChars,StringSplitOptions.RemoveEmptyEntries)[0]) / 1000
//let timeLrc = string.Format("[{0:00}:{1:00.00}]", timeKrc / 60, timeKrc % 60)