unit u_class_lyrics;
interface
uses
Windows, Classes, SysUtils, Contnrs, DateUtils;
type
TLyric = class
PlayTime : TDateTime;
Lyric : string;
end;
TLyricsThread = class(TThread)
private
FLyrics : TObjectList;
FPlayList : TObjectList;
FOnLyric : TNotifyEvent;
FStarted : Boolean;
FStartTime : TDateTime;
FStartOffset : Integer;
FLyricIndex : Integer;
FOnPlayEnd: TNotifyEvent;
procedure SyncOnLyric;
procedure SyncOnPlayEnd;
procedure DoOnLyric;
procedure DoOnPlayEnd;
procedure DoWork;
procedure LoadLyricsFile(Filename : String);
procedure LoadPlayList(Offset : Integer);
protected
procedure Execute; override;
public
ThreadDone : THandle;
constructor Create;
destructor Destroy; override;
procedure Play(LyricsFile : String; Offset : Integer);
published
property OnLyric : TNotifyEvent read FOnLyric write FOnLyric;
property OnPlayEnd : TNotifyEvent read FOnPlayEnd write FOnPlayEnd;
end;
implementation
{ TLyricsThread }
procedure TLyricsThread.SyncOnLyric;
begin
if Assigned(FOnLyric) then
Synchronize(DoOnLyric);
Inc(FLyricIndex);
end;
procedure TLyricsThread.SyncOnPlayEnd;
begin
if Assigned(FOnPlayEnd) then
Synchronize(DoOnPlayEnd);
end;
procedure TLyricsThread.DoOnLyric;
begin
FOnLyric(TLyric(FPlayList[FLyricIndex]));
end;
procedure TLyricsThread.DoOnPlayEnd;
begin
FOnPlayEnd(Self);
end;
procedure TLyricsThread.LoadLyricsFile(Filename: string);
var Index : Integer;
StrList : TStringList;
Line : string;
FLyric : TLyric;
FormatSettings : TFormatSettings;
begin
// this function assumes that the file is indeed a lyrics file with the correct format
FLyrics.Clear;
if FileExists(Filename) then
begin
StrList := TStringList.Create;
GetLocaleFormatSettings(LOCALE_SYSTEM_DEFAULT, FormatSettings);
FormatSettings.TimeSeparator := ':';
FormatSettings.DecimalSeparator := '.';
try
StrList.LoadFromFile(FileName);
Index := StrList.Count;
while Index > 0 do begin
Dec(Index);
Line := Trim(StrList[Index]);
if Line <> '' then begin
FLyric := TLyric.Create;
FLyric.PlayTime := StrToTime('00:' + Copy(Line, Pos('[', Line) + 1, Pos(']', Line) - Pos('[', Line) - 1),
FormatSettings);
FLyric.Lyric := Copy(Line, Pos(']', Line) + 1, MaxInt);
// add to our list, when the file is completely loaded the first Lyric has index(0)
FLyrics.Insert(0, FLyric);
end;
end;
finally
FreeAndNil(StrList);
end;
end;
end;
procedure TLyricsThread.LoadPlayList(Offset: Integer);
var Index : Integer;
Lyric : TLyric;
begin
// load only the lyrics we need to play
FPlayList.Clear;
Index := FLyrics.Count;
while Index > 0 do
begin
Dec(Index);
Lyric := TLyric(FLyrics[Index]);
if Lyric.PlayTime >= EncodeTime(0, 0, Offset, 0) then
FPlayList.Insert(0, Lyric);
end;
end;
procedure TLyricsThread.Play(LyricsFile : String; Offset: Integer);
begin
// play our lyrics file, with an eventual offset in seconds
LoadLyricsFile(LyricsFile);
LoadPlayList(Offset);
FStartOffset := OffSet;
FStartTime := Now;
FLyricIndex := 0;
FStarted := True;
end;
procedure TLyricsThread.DoWork;
var Lyric : TLyric;
T : TDateTime;
begin
if FStarted then
begin
if FLyricIndex > (FPlayList.Count - 1) then
begin
FStarted := False;
FLyricIndex := 0;
SyncOnPlayEnd;
end
else
begin
Lyric := TLyric(FPlayList[FLyricIndex]);
T := IncSecond(Now - FStartTime, FStartOffset);
if T >= Lyric.PlayTime then
SyncOnLyric;
end;
end;
end;
// main thread loop
procedure TLyricsThread.Execute;
begin
while not Terminated do
begin
Sleep(10); // 10 millisecond resolution should be enough
DoWork;
end;
SetEvent(ThreadDone);
end;
constructor TLyricsThread.Create;
begin
inherited Create(True);
FreeOnTerminate := False;
FStarted := False;
// create objects
FLyrics := TObjectList.Create(True);
FPlayList := TObjectList.Create(False);
ThreadDone := CreateEvent(nil, True, False, nil);
end;
destructor TLyricsThread.Destroy;
begin
// destroy objects
CloseHandle(ThreadDone);
FreeAndNil(FLyrics);
FreeAndNil(FPlayList);
inherited;
end;
end.