如何读取LRC歌词并显示出来,类似千千静听那种效果?就这点分了,感谢帮忙(150分)

  • 主题发起人 主题发起人 webyjh
  • 开始时间 开始时间
W

webyjh

Unregistered / Unconfirmed
GUEST, unregistred user!
如何读取LRC歌词并显示出来,类似千千静听那种效果?

附LRC歌词格式

[ti:雪中情]
[ar:刘锡明]
[al:忘情忘爱]
[by:aranlz]
[offset:500]
[00:05.00]词:罗助键/陈维祥
[00:10.00]曲:罗安
[00:15.00]专辑:忘情忘爱
[00:20.00]----歌词吾爱----
[00:25.00]www.51lrc.com ★ aranlz

[02:38.74][00:30.47]寒风萧萧 飞雪飘零
[02:46.63][00:38.47]长路漫漫 踏歌而行
[02:52.49][00:44.25]回首望星辰
[02:57.81][00:49.47]往事如烟云
[03:04.30][00:56.02]犹记别离时
[03:09.91][01:01.58]徒留雪中情
[03:17.41][01:09.39]雪中情 雪中情
[03:22.94][01:14.66]雪中梦未醒
[03:28.30][01:20.11]痴情换得 一生泪印
[03:36.89][01:28.62]雪中情 雪中情
[03:42.45][01:34.07]雪中我独行
[03:47.76][01:39.49]挥尽多少 英雄豪情
[03:58.81][01:50.76]惟有与你同行
[04:04.35][01:56.02]与你同行
[04:07.82][01:59.05]才能把梦追寻
 
type
PLyricLine = ^TLyricLine;
TLyricLine = record
MilliSeconds: Cardinal; //歌词在歌曲中按照毫秒计算的出现时间
Lyric: ShortString;
end;

TLyricLines = array of TLyricLine;

TLyricParsingEvent = procedure(Sender: TObject; Percent: Double) of object;

TCustomLyricParser = class(TObject)
private
FOnParsing: TLyricParsingEvent;
protected
procedure DoParsing(Percent: Double); dynamic;
function ParseTimeTextToMillisec(TimeText: string; var Millisec: Cardinal): Boolean;
procedure Sort(var LyricLines: TLyricLines); virtual;
public
procedure ParseFrom(var LyricLines: TLyricLines; SourceLyrics: TStrings); virtual; abstract;
procedure ParseTo(const LyricLines: TLyricLines; DestLyrics: TStrings); virtual; abstract;
property OnParsing: TLyricParsingEvent read FOnParsing write FOnParsing;
end;

ELyricLinesIsEmpty = class(Exception);
ELyricOutOfBounds = class(Exception);

TLyricParser = class(TCustomLyricParser)
private
function ParseLine(const LineText: string; var Time, Lyric: string): Integer;
protected
public
procedure ParseFrom(var LyricLines: TLyricLines; SourceLyrics: TStrings); override;
procedure ParseTo(const LyricLines: TLyricLines; DestLyrics: TStrings); override;
end;

implementation

uses UCommonProcs;

procedure InvertLyricLine(R1, R2: PLyricLine);
var
L: TLyricLine;
begin
L.MilliSeconds := R1^.MilliSeconds;
L.Lyric := R1^.Lyric;
R1^.MilliSeconds := R2^.MilliSeconds;
R1^.Lyric := R2^.Lyric;
R2^.MilliSeconds := L.MilliSeconds;
R2^.Lyric := L.Lyric;
end;

{ TCustomLyricParser }

procedure TCustomLyricParser.DoParsing(Percent: Double);
begin
if Assigned(FOnParsing) then FOnParsing(Self, Percent);
end;

function TCustomLyricParser.ParseTimeTextToMillisec(TimeText: string;
var Millisec: Cardinal): Boolean;
var
ttArray: array of Cardinal;
ttLen: Integer;
s: string;
c: Char;
begin
Result := False;
ttLen := 2;
SetLength(ttArray, ttLen + 1);
try
s := '';
while (Length(TimeText) > 0) do
begin
c := TimeText[Length(TimeText)];
if not (c in ['.', ':']) then
begin
if not (c in ['[', ']']) then
s := c + s
end
else begin
try
ttArray[ttLen] := StrToInt(s);
Result := True;
except
Result := False;
Exit;
end;
s := '';
Dec(ttLen);
end;
Delete(TimeText, Length(TimeText), 1);
end;
if s <> '' then
try
ttArray[0] := StrToInt(s);
except
Result := False;
Exit;
end;
if Result then
begin
Millisec := 0;
for ttLen := High(ttArray) downto 0 do
case ttLen of
2: //毫秒
begin
Millisec := ttArray[ttLen];
if (Millisec > 0) and (Millisec < 100) then
Millisec := Millisec * 10;
end;
1: //秒
Millisec := ttArray[ttLen] * 1000 + Millisec;
0: //分
Millisec := ttArray[ttLen] * 60 * 1000 + Millisec;
else
Result := False;
Break;
end;
end;
finally
ttArray := nil;
end;
end;

procedure TCustomLyricParser.Sort(var LyricLines: TLyricLines);

procedure QSort(var Lyric: TLyricLines; Left, Right: Word);
var
l, r, Pivot: Integer;
begin
l := Left;
r := Right;
Pivot := Lyric[(Left + Right) div 2].MilliSeconds;
while l < r do
begin
while Lyric[l].MilliSeconds < Pivot do Inc(l);
while Lyric[r].MilliSeconds > Pivot do Dec(r);
if l >= r then Break;
InvertLyricLine(@Lyric[l], @Lyric[r]);
if l <> Pivot then Dec(r);
if r <> Pivot then Inc(l);
end;
if l = r then Inc(l);
if Left < r then QSort(Lyric, Left, l + 1);
if l < Right then QSort(Lyric, r + 1, Right);
end; // 好像有bug,容易堆栈溢出

procedure Bubble(var Lyric: TLyricLines; Size: Integer);
var
I, Pass: Integer;
begin
for Pass := 1 to Size - 1 do
begin
I := 0;
while i < Size - Pass do
begin
if Lyric.MilliSeconds > Lyric[I + 1].MilliSeconds then
InvertLyricLine(@Lyric, @Lyric[I + 1]);
Inc(I);
end;
end;
end;

begin
Bubble(LyricLines, Length(LyricLines));
end;

{ TLyricParser }

procedure TLyricParser.ParseFrom(var LyricLines: TLyricLines;
SourceLyrics: TStrings);
var
iLine: Integer;
sTime, sSingleTime, sLyric: string;
ResMSec: Cardinal;
iPos: Integer;
begin
for iLine := 0 to SourceLyrics.Count - 1 do
begin
if ParseLine(Trim(SourceLyrics[iLine]), sTime, sLyric) = 0 then Continue;
repeat
iPos := Pos(']', sTime);
//Assert(iPos > 0, 'Assert: TLyricParser.ParseFrom::iPos<=0');
sSingleTime := Copy(sTime, 1, iPos);
//Assert(sSingleTime <> '', 'Assert: TLyricParser.ParseFrom::sSingleTime=''''');
Delete(sTime, 1, iPos);
if not ParseTimeTextToMillisec(Trim(sSingleTime), ResMSec) then Continue;
SetLength(LyricLines, Length(LyricLines) + 1);
with LyricLines[High(LyricLines)] do
begin
MilliSeconds := ResMSec;
Lyric := sLyric;
end;
until sTime = '';
DoParsing(GetPercentFrom(iLine + 1, SourceLyrics.Count));
end;
Sort(LyricLines);
end;

function TLyricParser.ParseLine(const LineText: string; var Time,
Lyric: string): Integer;
type
FoundArea = (faSplitFirst, faSplitLast, faText);
var
pIndex: Integer;
TempStr: string;
Area: FoundArea;
begin
Time := '';
TempStr := '';
Result := 0;
Area := faText;
for pIndex := 1 to Length(LineText) do
begin
TempStr := TempStr + LineText[pIndex];
case LineText[pIndex] of
'[':
Area := faSplitFirst;
']':
if Area = faSplitFirst then
begin
Area := faSplitLast;
Inc(Result);
Time := Time + TempStr;
TempStr := '';
end
else
Area := faText;
else // case else
if Area = faSplitLast then Area := faText;
end; // case
end; // for
Lyric := TempStr;
end;

procedure TLyricParser.ParseTo(const LyricLines: TLyricLines;
DestLyrics: TStrings);
var
iLine: Integer;
iLineTotal: Integer;
begin
iLineTotal := High(LyricLines);
for iLine := Low(LyricLines) to iLineTotal do
begin

DoParsing(GetPercentFrom(iLine, iLineTotal));
end;
end;
 
非常感谢,这个东东怎么用啊
 
UCommonProcs 为何物呀?
 
这是一个LRC文件的解析类,把文件读取并解析成一个多行歌词的数组 ,然后你用你需要的算法来显示到画布上即可。UCommonProcs是一个共用函数单元(项目里面的),不记得我这个代码里面用了UCommonProcs里面的什么函数,看了下,应该没用什么函数,这个引用可以去掉的。如果你发现没法编译的话,可以告诉我哪行有问题,我在去UCommonProcs里面拷贝过来。
 
DoParsing(GetPercentFrom(iLine + 1, SourceLyrics.Count));
是否有[blue]GetPercentFrom[/blue]这个函数的声明?

如果可以的话,把这个单元发给我,或单独贴出来

mail: cnzzlp#163.com (#改为@)
 
function GetPercentFrom(Int, Total: Cardinal): Double;
begin
if (Int = 0) or (Total = 0) then
Result := 0
else if Int = Total then
Result := 100
else
Result := Int / (Total / 100);
end;

[删除]呵呵,记错了函数了,刚刚也没仔细看,实际上这个是算百分比的函数。
 
我晕乎了,zqw0117您有没有空做个简单的demo出来呢[:D]
 
var
Lyrics: TLyricLines;
Parser: TLyricParser;
Strings: TStringList;
begin
Strings := TStringList.Create;
try
Strings.LoadFromFile('c:/abc.lrc'); //加载lrc文件
Parser := TLyricParser.Create;
try
Parser.ParseFrom(Lyrics, Strings); //从StringList里面加载lrc的文件并做解析,结果保存在Lyrics里面
// 现在Lyrics里面就是一个array,保存了每句歌词(并且还有歌词应该出现的毫秒).你可以写适当的代码把Lyrics里面当前时间应该显示的行算出来,然后画到你想画的画布上,即可.
finally
Parser.Free;
end;
finally
Strings.Free;
end;
end;
Demo很简单.直接用就可以了.本来我还打算做一个显示类的,不过最近没有时间,当初只做了读取分析(写入--即TParser.ParseTo方法--还没实现完呢),就直接画画布上了,赶工期也就没仔细写下去,回头有时间了再完善.
 
强烈感谢 钟Sir go.go.go...[:D]
 
啊,楼主您认识我?怎么知道我姓钟?呵呵,请问阁下大名:)或者有空来QQ聊聊:
185845281
上面是小弟的QQ号.
 
呵呵,QQ见,578544784,我网名清风
 
后退
顶部