aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/base/TextGL.pas150
-rw-r--r--src/base/UGraphic.pas12
-rw-r--r--src/base/ULyrics.pas765
-rw-r--r--src/screens/UScreenSing.pas19
4 files changed, 334 insertions, 612 deletions
diff --git a/src/base/TextGL.pas b/src/base/TextGL.pas
index 8569fadf..03351adf 100644
--- a/src/base/TextGL.pas
+++ b/src/base/TextGL.pas
@@ -37,7 +37,6 @@ uses
gl,
SDL,
UTexture,
-// SDL_ttf,
ULog;
procedure BuildFont; // build our bitmap font
@@ -45,6 +44,7 @@ procedure KillFont; // delete the font
function glTextWidth(text: PChar): real; // returns text width
procedure glPrintLetter(letter: char);
procedure glPrint(text: pchar); // custom GL "Print" routine
+procedure ResetFont(); // reset font settings of active font
procedure SetFontPos(X, Y: real); // sets X and Y
procedure SetFontZ(Z: real); // sets Z
procedure SetFontSize(Size: real);
@@ -209,13 +209,19 @@ function glTextWidth(text: pchar): real;
var
Letter: char;
i: integer;
+ Font: PFont;
begin
Result := 0;
+ Font := @Fonts[ActFont];
+
for i := 0 to Length(text) -1 do
begin
Letter := Text[i];
- Result := Result + Fonts[ActFont].Width[Ord(Letter)] * Fonts[ActFont].Tex.H / 30 * Fonts[ActFont].AspectW;
+ Result := Result + Font.Width[Ord(Letter)] * Font.Tex.H / 30 * Font.AspectW;
end;
+
+ if ((Result > 0) and Font.Italic) then
+ Result := Result + 12 * Font.Tex.H / 60 * Font.AspectW;
end;
procedure glPrintLetter(Letter: char);
@@ -332,6 +338,14 @@ begin
end;
end;
+procedure ResetFont();
+begin
+ SetFontPos(0, 0);
+ SetFontZ(0);
+ SetFontItalic(False);
+ SetFontReflection(False, 0);
+end;
+
procedure SetFontPos(X, Y: real);
begin
Fonts[ActFont].Tex.X := X;
@@ -374,136 +388,4 @@ begin
Fonts[ActFont].Blend := Enable;
end;
-
-
-
-(*
-<mog> I uncommented this, because it was some kind of after hour hack together with blindy
-it's actually just a prove of concept, as it's having some flaws
-- instead nice and clean ttf code should be placed here :)
-
-{$IFDEF FPC}
- {$ASMMODE Intel}
-{$ENDIF}
-
-function NextPowerOfTwo(Value: integer): integer;
-begin
- Result:= 1;
-{$IF Defined(CPUX86_64)}
- asm
- mov rcx, -1
- bsr rcx, Value
- inc rcx
- shl Result, cl
- end;
-{$ELSEIF Defined(CPU386) or Defined(CPUI386)}
- asm
- mov ecx, -1
- bsr ecx, Value
- inc ecx
- shl Result, cl
- end;
-{$ELSE}
- while (Result <= Value) do
- Result := 2 * Result;
-{$IFEND}
-end;
-
-function LoadFont(FileName: PAnsiChar; PointSize: integer):PTTF_Font;
-begin
- if (FileExists(FileName)) then
- begin
- Result := TTF_OpenFont( FileName, PointSize );
- end
- else
- begin
- Log.LogStatus('ERROR Could not find font in ' + FileName , '');
- ShowMessage( 'ERROR Could not find font in ' + FileName );
- Result := nil;
- end;
-end;
-
-function RenderText(font: PTTF_Font; Text:PAnsiChar; Color: Cardinal): PSDL_Surface;
-var
- clr : TSDL_color;
-begin
- clr.r := ((Color and $ff0000) shr 16 ) div 255;
- clr.g := ((Color and $ff00 ) shr 8 ) div 255;
- clr.b := ( Color and $ff ) div 255 ;
-
- result := TTF_RenderText_Blended( font, text, cLr);
-end;
-
-procedure printrandomtext();
-var
- stext,intermediary : PSDL_surface;
- clrFg, clrBG : TSDL_color;
- texture : Gluint;
- font : PTTF_Font;
- w,h : integer;
-begin
-
- font := LoadFont('fonts\comicbd.ttf', 42);
-
- clrFg.r := 255;
- clrFg.g := 255;
- clrFg.b := 255;
- clrFg.unused := 255;
-
- clrBg.r := 255;
- clrbg.g := 0;
- clrbg.b := 255;
- clrbg.unused := 0;
-
- sText := RenderText(font, 'katzeeeeeee', $fe198e);
- //sText := TTF_RenderText_Blended( font, 'huuuuuuuuuund', clrFG);
-
- // Convert the rendered text to a known format
- w := nextpoweroftwo(sText.w);
- h := nextpoweroftwo(sText.h);
-
- intermediary := SDL_CreateRGBSurface(0, w, h, 32,
- $000000ff, $0000ff00, $00ff0000, $ff000000);
-
- SDL_SetAlpha(intermediary, 0, 255);
- SDL_SetAlpha(sText, 0, 255);
- SDL_BlitSurface(sText, nil, intermediary, nil);
-
- glGenTextures(1, @texture);
-
- glBindTexture(GL_TEXTURE_2D, texture);
-
- glTexImage2D(GL_TEXTURE_2D, 0, 4, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, intermediary.pixels);
-// on big endian machines (powerpc) this may need to be changed to
-// Needs to be tests. KaMiSchi Sept 2008
-// glTexImage2D(GL_TEXTURE_2D, 0, 4, w, h, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, intermediary.pixels);
-
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-
- glEnable(GL_TEXTURE_2D);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- glEnable(GL_BLEND);
- glBindTexture(GL_TEXTURE_2D, texture);
- glColor4f(1, 0, 1, 1);
-
- glbegin(gl_quads);
- glTexCoord2f(0, 0); glVertex2f(200 , 300 );
- glTexCoord2f(0, sText.h/h); glVertex2f(200 , 300 + sText.h);
- glTexCoord2f(sText.w/w, sText.h/h); glVertex2f(200 + sText.w, 300 + sText.h);
- glTexCoord2f(sText.w/w, 0); glVertex2f(200 + sText.w, 300 );
- glEnd;
- glfinish();
- glDisable(GL_BLEND);
- gldisable(gl_texture_2d);
-
- SDL_FreeSurface(sText);
- SDL_FreeSurface(intermediary);
- glDeleteTextures(1, @texture);
- TTF_CloseFont(font);
-
-end;
-*)
-
-
end.
diff --git a/src/base/UGraphic.pas b/src/base/UGraphic.pas
index 1134333b..a6620c0d 100644
--- a/src/base/UGraphic.pas
+++ b/src/base/UGraphic.pas
@@ -232,7 +232,17 @@ const
Skin_OscG = 0;
Skin_OscB = 0;
- Skin_LyricsT = 494; // 500 / 510 / 400
+ // TODO: add to theme ini file
+ Skin_LyricsT = 493;
+ Skin_LyricsUpperX = 80;
+ Skin_LyricsUpperW = 640;
+ Skin_LyricsUpperY = Skin_LyricsT;
+ Skin_LyricsUpperH = 41;
+ Skin_LyricsLowerX = 80;
+ Skin_LyricsLowerW = 640;
+ Skin_LyricsLowerY = Skin_LyricsT + Skin_LyricsUpperH + 1;
+ Skin_LyricsLowerH = 41;
+
Skin_SpectrumT = 470;
Skin_SpectrumBot = 570;
Skin_SpectrumH = 100;
diff --git a/src/base/ULyrics.pas b/src/base/ULyrics.pas
index 46354cbe..ece9f1d4 100644
--- a/src/base/ULyrics.pas
+++ b/src/base/ULyrics.pas
@@ -44,32 +44,31 @@ type
// stores two textures for enabled/disabled states
TPlayerIconTex = array [0..1] of TTexture;
+ TLyricsEffect = (lfxSimple, lfxZoom, lfxSlide, lfxBall, lfxShift);
+
PLyricWord = ^TLyricWord;
TLyricWord = record
- X: Real; // left corner
- Width: Real; // width
- Start: Cardinal; // start of the word in quarters (beats)
- Length: Cardinal; // length of the word in quarters
- Text: String; // text
- Freestyle: Boolean; // is freestyle?
+ X: real; // left corner
+ Width: real; // width
+ Start: cardinal; // start of the word in quarters (beats)
+ Length: cardinal; // length of the word in quarters
+ Text: string; // text
+ Freestyle: boolean; // is freestyle?
end;
- ALyricWord = array of TLyricWord;
+ TLyricWordArray = array of TLyricWord;
TLyricLine = class
public
- Text: String; // text
- Tex: glUInt; // texture of the text
- Width: Real; // width
- Size: Byte; // fontsize
- Words: ALyricWord; // words in this line
- CurWord: Integer; // current active word idx (only valid if line is active)
- Start: Integer; // start of this line in quarters (Note: negative start values are possible due to gap)
- StartNote: Integer; // start of the first note of this line in quarters
- Length: Integer; // length in quarters (from start of first to the end of the last note)
- HasFreestyle: Boolean; // one or more word are freestyle?
- CountFreestyle: Integer; // how often there is a change from freestyle to non freestyle in this line
- Players: Byte; // players that should sing that line (bitset, Player1: 1, Player2: 2, Player3: 4)
- LastLine: Boolean; // is this the last line of the song?
+ Text: string; // text
+ Width: real; // width
+ Height: real; // height
+ Words: TLyricWordArray; // words in this line
+ CurWord: integer; // current active word idx (only valid if line is active)
+ Start: integer; // start of this line in quarters (Note: negative start values are possible due to gap)
+ StartNote: integer; // start of the first note of this line in quarters
+ Length: integer; // length in quarters (from start of first to the end of the last note)
+ Players: byte; // players that should sing that line (bitset, Player1: 1, Player2: 2, Player3: 4)
+ LastLine: boolean; // is this the last line of the song?
constructor Create();
destructor Destroy(); override;
@@ -78,7 +77,7 @@ type
TLyricEngine = class
private
- LastDrawBeat: Real;
+ LastDrawBeat: real;
UpperLine: TLyricLine; // first line displayed (top)
LowerLine: TLyricLine; // second lind displayed (bottom)
QueueLine: TLyricLine; // third line (will be displayed when lower line is finished)
@@ -86,80 +85,79 @@ type
IndicatorTex: TTexture; // texture for lyric indikator
BallTex: TTexture; // texture of the ball for the lyric effect
- QueueFull: Boolean; // set to true if the queue is full and a line will be replaced with the next AddLine
- LCounter: Word; // line counter
+ QueueFull: boolean; // set to true if the queue is full and a line will be replaced with the next AddLine
+ LCounter: integer; // line counter
// duet mode - textures for player icons
// FIXME: do not use a fixed player count, use MAX_PLAYERS instead
PlayerIconTex: array[0..5] of TPlayerIconTex;
- //Some helper Procedures for Lyric Drawing
- procedure DrawLyrics (Beat: Real);
- procedure DrawLyricsLine(X, W, Y: Real; Size: Byte; Line: TLyricLine; Beat: Real);
- procedure DrawPlayerIcon(Player: Byte; Enabled: Boolean; X, Y, Size, Alpha: Real);
- procedure DrawBall(XBall, YBall, Alpha:Real);
+ // Some helper procedures for lyric drawing
+ procedure DrawLyrics (Beat: real);
+ procedure UpdateLineMetrics(LyricLine: TLyricLine);
+ procedure DrawLyricsWords(LyricLine: TLyricLine; X, Y: real; StartWord, EndWord: integer);
+ procedure DrawLyricsLine(X, W, Y, H: real; Line: TLyricLine; Beat: real);
+ procedure DrawPlayerIcon(Player: byte; Enabled: boolean; X, Y: real; Size, Alpha: real);
+ procedure DrawBall(XBall, YBall, Alpha: real);
public
// positions, line specific settings
- UpperLineX: Real; //X Start Pos of UpperLine
- UpperLineW: Real; //Width of UpperLine with Icon(s) and Text
- UpperLineY: Real; //Y Start Pos of UpperLine
- UpperLineSize: Byte; //Max Size of Lyrics Text in UpperLine
+ UpperLineX: real; // X start-pos of UpperLine
+ UpperLineW: real; // Width of UpperLine with icon(s) and text
+ UpperLineY: real; // Y start-pos of UpperLine
+ UpperLineH: real; // Max. font-size of lyrics text in UpperLine
- LowerLineX: Real; //X Start Pos of LowerLine
- LowerLineW: Real; //Width of LowerLine with Icon(s) and Text
- LowerLineY: Real; //Y Start Pos of LowerLine
- LowerLineSize: Byte; //Max Size of Lyrics Text in LowerLine
+ LowerLineX: real; // X start-pos of LowerLine
+ LowerLineW: real; // Width of LowerLine with icon(s) and text
+ LowerLineY: real; // Y start-pos of LowerLine
+ LowerLineH: real; // Max. font-size of lyrics text in LowerLine
// display propertys
- LineColor_en: TRGBA; //Color of Words in an Enabled Line
- LineColor_dis: TRGBA; //Color of Words in a Disabled Line
- LineColor_act: TRGBA; //Color of teh active Word
- FontStyle: Byte; //Font for the Lyric Text
- FontReSize: Boolean; //ReSize Lyrics if they don't fit Screen
+ LineColor_en: TRGBA; // Color of words in an enabled line
+ LineColor_dis: TRGBA; // Color of words in a disabled line
+ LineColor_act: TRGBA; // Color of the active word
+ FontStyle: byte; // Font for the lyric text
{ // currently not used
- FadeInEffect: Byte; //Effect for Line Fading in: 0: No Effect; 1: Fade Effect; 2: Move Upwards from Bottom to Pos
- FadeOutEffect: Byte; //Effect for Line Fading out: 0: No Effect; 1: Fade Effect; 2: Move Upwards
+ FadeInEffect: byte; // Effect for line fading in: 0: No Effect; 1: Fade Effect; 2: Move Upwards from Bottom to Pos
+ FadeOutEffect: byte; // Effect for line fading out: 0: No Effect; 1: Fade Effect; 2: Move Upwards
}
- UseLinearFilter: Boolean; //Should Linear Tex Filter be used
-
// song specific settings
- BPM: Real;
- Resolution: Integer;
+ BPM: real;
+ Resolution: integer;
// properties to easily read options of this class
- property IsQueueFull: Boolean read QueueFull; // line in queue?
- property LineCounter: Word read LCounter; // lines that were progressed so far (after last clear)
+ property IsQueueFull: boolean read QueueFull; // line in queue?
+ property LineCounter: integer read LCounter; // lines that were progressed so far (after last clear)
procedure AddLine(Line: PLine); // adds a line to the queue, if there is space
- procedure Draw (Beat: Real); // draw the current (active at beat) lyrics
+ procedure Draw (Beat: real); // draw the current (active at beat) lyrics
// clears all cached song specific information
- procedure Clear(cBPM: Real = 0; cResolution: Integer = 0);
+ procedure Clear(cBPM: real = 0; cResolution: integer = 0);
function GetUpperLine(): TLyricLine;
function GetLowerLine(): TLyricLine;
- function GetUpperLineIndex(): Integer;
+ function GetUpperLineIndex(): integer;
- constructor Create; overload;
- constructor Create(ULX,ULY,ULW,ULS,LLX,LLY,LLW,LLS: Real); overload;
+ constructor Create(ULX, ULY, ULW, ULH, LLX, LLY, LLW, LLH: real);
procedure LoadTextures;
destructor Destroy; override;
end;
implementation
-uses SysUtils,
- USkins,
- TextGL,
- UGraphic,
- UDisplay,
- ULog,
- math,
- UIni;
+uses
+ SysUtils,
+ USkins,
+ TextGL,
+ UGraphic,
+ UDisplay,
+ ULog,
+ math,
+ UIni;
{ TLyricLine }
@@ -190,20 +188,17 @@ begin
SetLength(Words, 0);
CurWord := -1;
-
- HasFreestyle := False;
- CountFreestyle := 0;
end;
{ TLyricEngine }
-//---------------
-// Create - Constructor, just get Memory
-//---------------
-constructor TLyricEngine.Create;
+{**
+ * Initializes the engine.
+ *}
+constructor TLyricEngine.Create(ULX, ULY, ULW, ULH, LLX, LLY, LLW, LLH: real);
begin
- inherited;
+ inherited Create();
BPM := 0;
Resolution := 0;
@@ -214,31 +209,25 @@ begin
LowerLine := TLyricLine.Create;
QueueLine := TLyricLine.Create;
- UseLinearFilter := True;
LastDrawBeat := 0;
-end;
-constructor TLyricEngine.Create(ULX,ULY,ULW,ULS,LLX,LLY,LLW,LLS:Real);
-begin
- Create;
-
UpperLineX := ULX;
UpperLineW := ULW;
UpperLineY := ULY;
- UpperLineSize := Trunc(ULS);
+ UpperLineH := ULH;
LowerLineX := LLX;
LowerLineW := LLW;
LowerLineY := LLY;
- LowerLineSize := Trunc(LLS);
+ LowerLineH := LLH;
LoadTextures;
end;
-//---------------
-// Destroy - Frees Memory
-//---------------
+{**
+ * Frees memory.
+ *}
destructor TLyricEngine.Destroy;
begin
UpperLine.Free;
@@ -247,10 +236,10 @@ begin
inherited;
end;
-//---------------
-// Clear - Clears all cached Song specific Information
-//---------------
-procedure TLyricEngine.Clear(cBPM: Real; cResolution: Integer);
+{**
+ * Clears all cached Song specific Information.
+ *}
+procedure TLyricEngine.Clear(cBPM: real; cResolution: integer);
begin
BPM := cBPM;
Resolution := cResolution;
@@ -261,31 +250,14 @@ begin
end;
-//---------------
-// LoadTextures - Load Player Textures and Create Lyric Textures
-//---------------
+{**
+ * Loads textures needed for the drawing the lyrics,
+ * player icons, a ball for the ball effect and the lyric indicator.
+ *}
procedure TLyricEngine.LoadTextures;
var
I: Integer;
-
- function CreateLineTex: glUint;
- begin
- // generate and bind Texture
- glGenTextures(1, @Result);
- glBindTexture(GL_TEXTURE_2D, Result);
-
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-
- if UseLinearFilter then
- begin
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- end;
- end;
-
begin
-
// lyric indicator (bar that indicates when the line start)
IndicatorTex := Texture.LoadTexture(Skin.GetTextureFileName('LyricHelpBar'), TEXTURE_TYPE_TRANSPARENT, $FF00FF);
@@ -298,42 +270,21 @@ begin
PlayerIconTex[I][0] := Texture.LoadTexture(Skin.GetTextureFileName('LyricIcon_P' + InttoStr(I+1)), TEXTURE_TYPE_TRANSPARENT, 0);
PlayerIconTex[I][1] := Texture.LoadTexture(Skin.GetTextureFileName('LyricIconD_P' + InttoStr(I+1)), TEXTURE_TYPE_TRANSPARENT, 0);
end;
-
- // create line textures
- UpperLine.Tex := CreateLineTex;
- LowerLine.Tex := CreateLineTex;
- QueueLine.Tex := CreateLineTex;
end;
-
-//---------------
-// AddLine - Adds LyricLine to queue
-// The LyricEngine stores three lines in its queue:
-// UpperLine: the upper line displayed in the lyrics
-// LowerLine: the lower line displayed in the lyrics
-// QueueLine: an offscreen line that precedes LowerLine
-// If the queue is full the next call to AddLine will replace UpperLine with
-// LowerLine, LowerLine with QueueLine and QueueLine with the Line parameter.
-//---------------
+{**
+ * Adds LyricLine to queue.
+ * The LyricEngine stores three lines in its queue:
+ * UpperLine: the upper line displayed in the lyrics
+ * LowerLine: the lower line displayed in the lyrics
+ * QueueLine: an offscreen line that precedes LowerLine
+ * If the queue is full the next call to AddLine will replace UpperLine with
+ * LowerLine, LowerLine with QueueLine and QueueLine with the Line parameter.
+ *}
procedure TLyricEngine.AddLine(Line: PLine);
var
LyricLine: TLyricLine;
- PosX: Real;
- I: Integer;
- CurWord: PLyricWord;
- RenderPass: Integer;
-
- function CalcWidth(LyricLine: TLyricLine): Real;
- begin
- Result := glTextWidth(PChar(LyricLine.Text));
-
- Result := Result + (LyricLine.CountFreestyle * 10);
-
- // if the line ends with a freestyle not, then leave the place to finish to draw the text italic
- if (LyricLine.Words[High(LyricLine.Words)].Freestyle) then
- Result := Result + 12;
- end;
-
+ I: integer;
begin
// only add lines, if there is space
if not IsQueueFull then
@@ -352,7 +303,7 @@ begin
end
else
begin // rotate lines (round-robin-like)
- LyricLine := UpperLine;
+ LyricLine := UpperLine;
UpperLine := LowerLine;
LowerLine := QueueLine;
QueueLine := LyricLine;
@@ -381,160 +332,42 @@ begin
LyricLine.Words[I].Text := Line.Note[I].Text;
LyricLine.Words[I].Freestyle := Line.Note[I].NoteType = ntFreestyle;
- LyricLine.HasFreestyle := LyricLine.HasFreestyle or LyricLine.Words[I].Freestyle;
- LyricLine.Text := LyricLine.Text + LyricLine.Words[I].Text;
-
- if (I > 0) and
- LyricLine.Words[I-1].Freestyle and
- not LyricLine.Words[I].Freestyle then
- begin
- Inc(LyricLine.CountFreestyle);
- end;
- end;
-
- // set font params
- SetFontStyle(FontStyle);
- SetFontPos(0, 0);
- LyricLine.Size := UpperLineSize;
- SetFontSize(LyricLine.Size);
- SetFontItalic(False);
- SetFontReflection(False, 0);
- glColor4f(1, 1, 1, 1);
-
- // change fontsize to fit the screen
- LyricLine.Width := CalcWidth(LyricLine);
- while (LyricLine.Width > UpperLineW) do
- begin
- Dec(LyricLine.Size);
-
- if (LyricLine.Size <=1) then
- Break;
-
- SetFontSize(LyricLine.Size);
- LyricLine.Width := CalcWidth(LyricLine);
+ LyricLine.Text := LyricLine.Text + LyricLine.Words[I].Text;
end;
- // Offscreen rendering of LyricTexture:
- // First we will create a white transparent background to draw on.
- // If the text was simply drawn to the screen with blending, the translucent
- // parts of the text would be merged with the current color of the background.
- // This would result in a texture that partly contains the screen we are
- // drawing on. This will be visible in the fonts outline when the LyricTexture
- // is drawn to the screen later.
- // So we have to draw the text in TWO passes.
- // At the first pass we copy the characters to the back-buffer in such a way
- // that preceding characters are not repainted by following chars (otherwise
- // some characters would be blended twice in the 2nd pass).
- // To achieve this we disable blending and enable the depth-test. The z-buffer
- // is used as a mask or replacement for the missing stencil-buffer. A z-value
- // of 1 means the pixel was not assigned yet, whereas 0 stands for a pixel
- // that was already drawn on. In addition we set the depth-func in such a way
- // that assigned pixels (0) will not be drawn a second time.
- // At the second pass we draw the text with blending and without depth-test.
- // This will blend overlapping characters but not the background as it was
- // repainted in the first pass.
-
- glPushAttrib(GL_VIEWPORT_BIT or GL_DEPTH_BUFFER_BIT);
- glViewPort(0, 0, 800, 600);
- glClearColor(0, 0, 0, 0);
- glDepthRange(0, 1);
- glClearDepth(1);
- glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
-
- SetFontZ(0);
-
- // assure blending is off and the correct depth-func is enabled
- glDisable(GL_BLEND);
- glDepthFunc(GL_LESS);
-
- // we need two passes to draw the font onto the screen.
- for RenderPass := 0 to 1 do
- begin
- if (RenderPass = 0) then
- begin
- // first pass: simply copy each character to the screen without overlapping.
- SetFontBlend(false);
- glEnable(GL_DEPTH_TEST);
- end
- else
- begin
- // second pass: now we will blend overlapping characters.
- SetFontBlend(true);
- glDisable(GL_DEPTH_TEST);
- end;
-
- PosX := 0;
-
- // set word positions and line size and draw the line to the back-buffer
- for I := 0 to High(LyricLine.Words) do
- begin
- CurWord := @LyricLine.Words[I];
-
- SetFontItalic(CurWord.Freestyle);
-
- CurWord.X := PosX;
-
- // Draw Lyrics
- SetFontPos(PosX, 0);
- glPrint(PChar(CurWord.Text));
-
- CurWord.Width := glTextWidth(PChar(CurWord.Text));
- if CurWord.Freestyle then
- begin
- if (I < High(LyricLine.Words)) and not LyricLine.Words[I+1].Freestyle then
- CurWord.Width := CurWord.Width + 10
- else if (I = High(LyricLine.Words)) then
- CurWord.Width := CurWord.Width + 12;
- end;
- PosX := PosX + CurWord.Width;
- end;
- end;
-
- // copy back-buffer to texture
- glBindTexture(GL_TEXTURE_2D, LyricLine.Tex);
- // FIXME: this does not work this way
- glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 600-64, 1024, 64, 0);
- if (glGetError() <> GL_NO_ERROR) then
- Log.LogError('Creation of Lyrics-texture failed', 'TLyricEngine.AddLine');
-
- // restore OpenGL state
- glPopAttrib();
-
- // clear buffer (use white to avoid flimmering if no cover/video is available)
- glClearColor(1, 1, 1, 1);
- glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
- end; // if (Line <> nil) and (Length(Line.Note) > 0)
+ UpdateLineMetrics(LyricLine);
+ end;
// increase the counter
Inc(LCounter);
end;
-
-//---------------
-// Draw - Procedure Draws Lyrics; Beat is curent Beat in Quarters
-// Draw just manage the Lyrics, drawing is done by a call of DrawLyrics
-//---------------
-procedure TLyricEngine.Draw(Beat: Real);
+{**
+ * Draws Lyrics.
+ * Draw just manages the Lyrics, drawing is done by a call of DrawLyrics.
+ * @param Beat: current Beat in Quarters
+ *}
+procedure TLyricEngine.Draw(Beat: real);
begin
DrawLyrics(Beat);
LastDrawBeat := Beat;
end;
-//---------------
-// DrawLyrics(private) - Helper for Draw; main Drawing procedure
-//---------------
-procedure TLyricEngine.DrawLyrics(Beat: Real);
+{**
+ * Main Drawing procedure.
+ *}
+procedure TLyricEngine.DrawLyrics(Beat: real);
begin
- DrawLyricsLine(UpperLineX, UpperLineW, UpperlineY, 15, Upperline, Beat);
- DrawLyricsLine(LowerLineX, LowerLineW, LowerlineY, 15, Lowerline, Beat);
+ DrawLyricsLine(UpperLineX, UpperLineW, UpperLineY, UpperLineH, UpperLine, Beat);
+ DrawLyricsLine(LowerLineX, LowerLineW, LowerLineY, LowerLineH, LowerLine, Beat);
end;
-//---------------
-// DrawPlayerIcon(private) - Helper for Draw; Draws a Playericon
-//---------------
-procedure TLyricEngine.DrawPlayerIcon(Player: Byte; Enabled: Boolean; X, Y, Size, Alpha: Real);
+{**
+ * Draws a Player's icon.
+ *}
+procedure TLyricEngine.DrawPlayerIcon(Player: byte; Enabled: boolean; X, Y: real; Size, Alpha: real);
var
- IEnabled: Byte;
+ IEnabled: byte;
begin
if Enabled then
IEnabled := 0
@@ -558,10 +391,10 @@ begin
glDisable(GL_TEXTURE_2D);
end;
-//---------------
-// DrawBall(private) - Helper for Draw; Draws the Ball over the LyricLine if needed
-//---------------
-procedure TLyricEngine.DrawBall(XBall, YBall, Alpha: Real);
+{**
+ * Draws the Ball over the LyricLine if needed.
+ *}
+procedure TLyricEngine.DrawBall(XBall, YBall, Alpha: real);
begin
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
@@ -580,41 +413,122 @@ begin
glDisable(GL_TEXTURE_2D);
end;
-//---------------
-// DrawLyricsLine(private) - Helper for Draw; Draws one LyricLine
-//---------------
-procedure TLyricEngine.DrawLyricsLine(X, W, Y: Real; Size: Byte; Line: TLyricLine; Beat: Real);
+procedure TLyricEngine.DrawLyricsWords(LyricLine: TLyricLine;
+ X, Y: real; StartWord, EndWord: integer);
var
- CurWordStart, CurWordEnd: Real; // screen coordinates of current word and the rest of the sentence
- FreestyleDiff: Integer; // difference between top and bottom coordiantes for freestyle lyrics
- Progress: Real; // progress of singing the current word
- LyricX: Real; // left
- LyricX2: Real; // right
- LyricY: Real; // top
- LyricsHeight: Real; // height the lyrics are displayed
- Alpha: Real; // alphalevel to fade out at end
- CurWord, LastWord: PLyricWord; // current word
+ I: integer;
+ PosX: real;
+ CurWord: PLyricWord;
+begin
+ PosX := X;
+
+ // set word positions and line size and draw the line
+ for I := StartWord to EndWord do
+ begin
+ CurWord := @LyricLine.Words[I];
+ SetFontItalic(CurWord.Freestyle);
+ SetFontPos(PosX, Y);
+ glPrint(PChar(CurWord.Text));
+ PosX := PosX + CurWord.Width;
+ end;
+end;
+procedure TLyricEngine.UpdateLineMetrics(LyricLine: TLyricLine);
+var
+ I: integer;
+ PosX: real;
+ CurWord: PLyricWord;
+ RequestWidth, RequestHeight: real;
+begin
+ PosX := 0;
+
+ // setup font
+ SetFontStyle(FontStyle);
+ ResetFont();
+
+ // check if line is lower or upper line and set sizes accordingly
+ // Note: at the moment upper and lower lines have same width/height
+ // and this function is just called by AddLine() but this may change
+ // so that it is called by DrawLyricsLine().
+ //if (LyricLine = LowerLine) then
+ //begin
+ // RequestWidth := LowerLineW;
+ // RequestHeight := LowerLineH;
+ //end
+ //else
+ //begin
+ RequestWidth := UpperLineW;
+ RequestHeight := UpperLineH;
+ //end;
+
+ // set font size to a reasonable value
+ LyricLine.Height := RequestHeight * 0.9;
+ SetFontSize(LyricLine.Height/3);
+ LyricLine.Width := glTextWidth(PChar(LyricLine.Text));
+
+ // change font-size to fit into the lyric bar
+ if (LyricLine.Width > RequestWidth) then
+ begin
+ LyricLine.Height := Trunc(LyricLine.Height * (RequestWidth / LyricLine.Width));
+ // the line is very loooong, set font to at least 1px
+ if (LyricLine.Height < 1) then
+ LyricLine.Height := 1;
+
+ SetFontSize(LyricLine.Height/3);
+ LyricLine.Width := glTextWidth(PChar(LyricLine.Text));
+ end;
+
+ // calc word positions and widths
+ for I := 0 to High(LyricLine.Words) do
+ begin
+ CurWord := @LyricLine.Words[I];
+
+ // - if current word is italic but not the next word get the width of the
+ // italic font to avoid overlapping.
+ // - if two italic words follow each other use the normal style's
+ // width otherwise the spacing between the words will be too big.
+ // - if it is the line's last word use normal width
+ if CurWord.Freestyle and
+ (I+1 < Length(LyricLine.Words)) and
+ (not LyricLine.Words[I+1].Freestyle) then
+ begin
+ SetFontItalic(true);
+ end;
+
+ CurWord.X := PosX;
+ CurWord.Width := glTextWidth(PChar(CurWord.Text));
+ PosX := PosX + CurWord.Width;
+ SetFontItalic(false);
+ end;
+end;
+
+
+{**
+ * Draws one LyricLine
+ *}
+procedure TLyricEngine.DrawLyricsLine(X, W, Y, H: real; Line: TLyricLine; Beat: real);
+var
+ CurWord: PLyricWord; // current word
+ LastWord: PLyricWord; // last word in line
+ NextWord: PLyricWord; // word following current word
+ Progress: real; // progress of singing the current word
+ LyricX, LyricY: real; // left/top lyric position
+ WordY: real; // word y-position
+ LyricsEffect: TLyricsEffect;
+ Alpha: real; // alphalevel to fade out at end
+ ClipPlaneEq: array[0..3] of GLdouble; // clipping plane for slide effect
{// duet mode
- IconSize: Real; // size of player icons
- IconAlpha: Real; // alpha level of player icons
+ IconSize: real; // size of player icons
+ IconAlpha: real; // alpha level of player icons
}
begin
// do not draw empty lines
- // Note: lines with no words in it do not have a valid texture
- if (Length(Line.Words) = 0) or
- (Line.Width <= 0) then
- begin
+ if (Length(Line.Words) = 0) then
Exit;
- end;
-
- // this is actually a bit more than the real font size
- // it helps adjusting the "zoom-center"
- LyricsHeight := 30.5 * (Line.Size/10);
{
// duet mode
- IconSize := (2 * Size);
+ IconSize := (2 * Height);
IconAlpha := Frac(Beat/(Resolution*4));
DrawPlayerIcon (0, True, X, Y + (42 - IconSize) / 2 , IconSize, IconAlpha);
@@ -622,13 +536,19 @@ begin
DrawPlayerIcon (2, True, X + (IconSize + 1)*2, Y + (42 - IconSize) / 2, IconSize, IconAlpha);
}
- LyricX := X + W/2 - Line.Width/2;
- LyricX2 := LyricX + Line.Width;
+ // set font size and style
+ SetFontStyle(FontStyle);
+ ResetFont();
+ SetFontSize(Line.Height/3);
+ glColor4f(1, 1, 1, 1);
- // maybe center smaller lines
- //LyricY := Y;
- LyricY := Y + ((Size / Line.Size - 1) * LyricsHeight) / 2;
+ // center lyrics
+ LyricX := X + (W - Line.Width) / 2;
+ LyricY := Y + (H - Line.Height) / 2;
+ // get lyrics effect
+ LyricsEffect := TLyricsEffect(Ini.LyricsEffect);
+ // TODO: what about alpha in freetype outline fonts?
Alpha := 1;
// check if this line is active (at least its first note must be active)
@@ -648,18 +568,20 @@ begin
Inc(Line.CurWord);
end;
- FreestyleDiff := 0;
-
// determine current and last word in this line.
// If the end of the line is reached use the last word as current word.
LastWord := @Line.Words[High(Line.Words)];
CurWord := @Line.Words[Line.CurWord];
+ if (Line.CurWord+1 < Length(Line.Words)) then
+ NextWord := @Line.Words[Line.CurWord+1]
+ else
+ NextWord := nil;
// calc the progress of the lyrics effect
Progress := (Beat - CurWord.Start) / CurWord.Length;
- if Progress >= 1 then
+ if (Progress >= 1) then
Progress := 1;
- if Progress <= 0 then
+ if (Progress <= 0) then
Progress := 0;
// last word of this line finished, but this line did not hide -> fade out
@@ -671,157 +593,93 @@ begin
Alpha := 0;
end;
- // determine the start-/end positions of the fragment of the current word
- CurWordStart := CurWord.X;
- CurWordEnd := CurWord.X + CurWord.Width;
-
- // Slide Effect
- // simply paint the active texture to the current position
- if Ini.LyricsEffect = 2 then
- begin
- CurWordStart := CurWordStart + CurWord.Width * Progress;
- CurWordEnd := CurWordStart;
- end;
-
- if CurWord.Freestyle then
- begin
- if (Line.CurWord < High(Line.Words)) and
- (not Line.Words[Line.CurWord + 1].Freestyle) then
- begin
- FreestyleDiff := 2;
- end
- else
- begin
- FreestyleDiff := 12;
- CurWordStart := CurWordStart - 1;
- CurWordEnd := CurWordEnd - 2;
- end;
- end;
-
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- glEnable(GL_TEXTURE_2D);
- glBindTexture(GL_TEXTURE_2D, Line.Tex);
-
- // draw sentence up to current word
- // type 0: simple lyric effect
- // type 3: ball lyric effect
- // type 4: shift lyric effect
- if (Ini.LyricsEffect in [0, 3, 4]) then
- // ball lyric effect - only highlight current word and not that ones before in this line
+ // draw sentence before current word
+ if (LyricsEffect in [lfxSimple, lfxBall, lfxShift]) then
+ // only highlight current word and not that ones before in this line
glColorRGB(LineColor_en, Alpha)
else
glColorRGB(LineColor_act, Alpha);
+ DrawLyricsWords(Line, LyricX, LyricY, 0, Line.CurWord-1);
- glBegin(GL_QUADS);
- glTexCoord2f(0, 1);
- glVertex2f(LyricX, LyricY);
-
- glTexCoord2f(0, 1-LyricsHeight/64);
- glVertex2f(LyricX, LyricY + LyricsHeight);
-
- glTexCoord2f(CurWordStart/1024, 1-LyricsHeight/64);
- glVertex2f(LyricX + CurWordStart, LyricY + LyricsHeight);
-
- glTexCoord2f((CurWordStart + FreestyleDiff)/1024, 1);
- glVertex2f(LyricX + CurWordStart + FreestyleDiff, LyricY);
- glEnd;
-
- // draw rest of sentence
+ // draw rest of sentence (without current word)
glColorRGB(LineColor_en, Alpha);
- glBegin(GL_QUADS);
- glTexCoord2f((CurWordEnd + FreestyleDiff)/1024, 1);
- glVertex2f(LyricX + CurWordEnd + FreestyleDiff, LyricY);
-
- glTexCoord2f(CurWordEnd/1024, 1-LyricsHeight/64);
- glVertex2f(LyricX + CurWordEnd, LyricY + LyricsHeight);
-
- glTexCoord2f(Line.Width/1024, 1-LyricsHeight/64);
- glVertex2f(LyricX2, LyricY + LyricsHeight);
-
- glTexCoord2f(Line.Width/1024, 1);
- glVertex2f(LyricX2, LyricY);
- glEnd;
-
- // draw active word:
- // type 0: simple lyric effect
- // type 3: ball lyric effect
- // type 4: shift lyric effect
- // only change the color of the current word
- if (Ini.LyricsEffect in [0, 3, 4]) then
+ if (NextWord <> nil) then
begin
- if (Ini.LyricsEffect = 4) then
- LyricY := LyricY - 8 * (1-Progress);
-
- glColor4f(LineColor_act.r, LineColor_act.g, LineColor_act.b, Alpha);
- glBegin(GL_QUADS);
- glTexCoord2f((CurWordStart + FreestyleDiff)/1024, 1);
- glVertex2f(LyricX + CurWordStart + FreestyleDiff, LyricY);
-
- glTexCoord2f(CurWordStart/1024, 0);
- glVertex2f(LyricX + CurWordStart, LyricY + 64);
-
- glTexCoord2f(CurWordEnd/1024, 0);
- glVertex2f(LyricX + CurWordEnd, LyricY + 64);
+ DrawLyricsWords(Line, LyricX + NextWord.X, LyricY,
+ Line.CurWord+1, High(Line.Words));
+ end;
- glTexCoord2f((CurWordEnd + FreestyleDiff)/1024, 1);
- glVertex2f(LyricX + CurWordEnd + FreestyleDiff, LyricY);
- glEnd;
+ // draw current word
+ if (LyricsEffect in [lfxSimple, lfxBall, lfxShift]) then
+ begin
+ if (LyricsEffect = lfxShift) then
+ WordY := LyricY - 8 * (1-Progress)
+ else
+ WordY := LyricY;
- if (Ini.LyricsEffect = 4) then
- LyricY := LyricY + 8 * (1-Progress);
+ // change the color of the current word
+ glColor4f(LineColor_act.r, LineColor_act.g, LineColor_act.b, Alpha);
+ DrawLyricsWords(Line, LyricX + CurWord.X, WordY, Line.CurWord, Line.CurWord);
end
-
- // draw active word:
- // type 1: zoom lyric effect
// change color and zoom current word
- else if Ini.LyricsEffect = 1 then
+ else if (LyricsEffect = lfxZoom) then
begin
glPushMatrix;
- glTranslatef(LyricX + CurWordStart + (CurWordEnd-CurWordStart)/2,
- LyricY + LyricsHeight/2, 0);
-
- // set current zoom factor
+ // zoom at word center
+ glTranslatef(LyricX + CurWord.X + CurWord.Width/2,
+ LyricY + Line.Height/2, 0);
glScalef(1.0 + (1-Progress) * 0.5, 1.0 + (1-Progress) * 0.5, 1.0);
glColor4f(LineColor_act.r, LineColor_act.g, LineColor_act.b, Alpha);
- glBegin(GL_QUADS);
- glTexCoord2f((CurWordStart + FreestyleDiff)/1024, 1);
- glVertex2f(-(CurWordEnd-CurWordStart)/2 + FreestyleDiff, -LyricsHeight/2);
+ DrawLyricsWords(Line, -CurWord.Width/2, -Line.Height/2, Line.CurWord, Line.CurWord);
+
+ glPopMatrix;
+ end
+ // split current word into active and non-active part
+ else if (LyricsEffect = lfxSlide) then
+ begin
+ // enable clipping and set clip equation coefficients to zeros
+ glEnable(GL_CLIP_PLANE0);
+ FillChar(ClipPlaneEq[0], SizeOf(ClipPlaneEq), 0);
- glTexCoord2f(CurWordStart/1024, 1-LyricsHeight/64);
- glVertex2f(-(CurWordEnd-CurWordStart)/2, LyricsHeight/2);
+ glPushMatrix;
+ glTranslatef(LyricX + CurWord.X, LyricY, 0);
- glTexCoord2f(CurWordEnd/1024, 1-LyricsHeight/64);
- glVertex2f((CurWordEnd-CurWordStart)/2, LyricsHeight/2);
+ // clip non-active right part of the current word
+ ClipPlaneEq[0] := -1;
+ ClipPlaneEq[3] := CurWord.Width * Progress;
+ glClipPlane(GL_CLIP_PLANE0, @ClipPlaneEq);
+ // and draw active left part
+ glColor4f(LineColor_act.r, LineColor_act.g, LineColor_act.b, Alpha);
+ DrawLyricsWords(Line, 0, 0, Line.CurWord, Line.CurWord);
- glTexCoord2f((CurWordEnd + FreestyleDiff)/1024, 1);
- glVertex2f((CurWordEnd-CurWordStart)/2 + FreestyleDiff, -LyricsHeight/2);
- glEnd;
+ // clip active left part of the current word
+ ClipPlaneEq[0] := -ClipPlaneEq[0];
+ ClipPlaneEq[3] := -ClipPlaneEq[3];
+ glClipPlane(GL_CLIP_PLANE0, @ClipPlaneEq);
+ // and draw non-active right part
+ glColor4f(LineColor_en.r, LineColor_en.g, LineColor_en.b, Alpha);
+ DrawLyricsWords(Line, 0, 0, Line.CurWord, Line.CurWord);
glPopMatrix;
- end;
- glDisable(GL_TEXTURE_2D);
- glDisable(GL_BLEND);
+ glDisable(GL_CLIP_PLANE0);
+ end;
- // type 3: ball lyric effect
- if Ini.LyricsEffect = 3 then
+ // draw the ball onto the current word
+ if (LyricsEffect = lfxBall) then
begin
- DrawBall(LyricX + CurWordStart + (CurWordEnd-CurWordStart) * Progress,
+ glDisable(GL_TEXTURE_2D);
+ glDisable(GL_BLEND);
+ DrawBall(LyricX + CurWord.X + CurWord.Width * Progress,
LyricY - 15 - 15*sin(Progress * Pi), Alpha);
end;
end
else
begin
// this section is called if the whole line can be drawn at once and no
- // word has to be emphasized.
-
- glEnable(GL_TEXTURE_2D);
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- glBindTexture(GL_TEXTURE_2D, Line.Tex);
+ // word is highlighted.
// enable the upper, disable the lower line
if (Line = UpperLine) then
@@ -829,45 +687,30 @@ begin
else
glColorRGB(LineColor_dis);
- glBegin(GL_QUADS);
- glTexCoord2f(0, 1);
- glVertex2f(LyricX, LyricY);
-
- glTexCoord2f(0, 1-LyricsHeight/64);
- glVertex2f(LyricX, LyricY + LyricsHeight);
-
- glTexCoord2f(Line.Width/1024, 1-LyricsHeight/64);
- glVertex2f(LyricX2, LyricY + LyricsHeight);
-
- glTexCoord2f(Line.Width/1024, 1);
- glVertex2f(LyricX2, LyricY);
- glEnd;
-
- glDisable(GL_BLEND);
- glDisable(GL_TEXTURE_2D);
+ DrawLyricsWords(Line, LyricX, LyricY, 0, High(Line.Words));
end;
end;
-//---------------
-// GetUpperLine() - Returns a reference to the upper line
-//---------------
+{**
+ * @returns a reference to the upper line
+ *}
function TLyricEngine.GetUpperLine(): TLyricLine;
begin
Result := UpperLine;
end;
-//---------------
-// GetLowerLine() - Returns a reference to the lower line
-//---------------
+{**
+ * @returns a reference to the lower line
+ *}
function TLyricEngine.GetLowerLine(): TLyricLine;
begin
Result := LowerLine;
end;
-//---------------
-// GetUpperLineIndex() - Returns the index of the upper line
-//---------------
-function TLyricEngine.GetUpperLineIndex(): Integer;
+{**
+ * @returns the index of the upper line
+ *}
+function TLyricEngine.GetUpperLineIndex(): integer;
const
QUEUE_SIZE = 3;
begin
diff --git a/src/screens/UScreenSing.pas b/src/screens/UScreenSing.pas
index d0908ff8..1267bab8 100644
--- a/src/screens/UScreenSing.pas
+++ b/src/screens/UScreenSing.pas
@@ -298,7 +298,9 @@ begin
//<note>Pausepopup is not visibile at the beginning</note>
Static[StaticPausePopup].Visible := False;
- Lyrics := TLyricEngine.Create(80, Skin_LyricsT, 640, 12, 80, Skin_LyricsT + 36, 640, 12);
+ Lyrics := TLyricEngine.Create(
+ Skin_LyricsUpperX, Skin_LyricsUpperY, Skin_LyricsUpperW, Skin_LyricsUpperH,
+ Skin_LyricsLowerX, Skin_LyricsLowerY, Skin_LyricsLowerW, Skin_LyricsLowerH);
LyricsSync := TLyricsSyncSource.Create();
end;
@@ -550,8 +552,6 @@ begin
case Ini.LyricsFont of
0:
begin
- Lyrics.UpperLineSize := 14;
- Lyrics.LowerLineSize := 14;
Lyrics.FontStyle := 0;
Lyrics.LineColor_en.R := Skin_FontR;
@@ -571,8 +571,6 @@ begin
end;
1:
begin
- Lyrics.UpperLineSize := 14;
- Lyrics.LowerLineSize := 14;
Lyrics.FontStyle := 2;
Lyrics.LineColor_en.R := 0.75;
@@ -592,8 +590,6 @@ begin
end;
2:
begin
- Lyrics.UpperLineSize := 12;
- Lyrics.LowerLineSize := 12;
Lyrics.FontStyle := 3;
Lyrics.LineColor_en.R := 0.75;
@@ -970,15 +966,6 @@ begin
else
Lyrics.AddLine(nil);
end;
-
- // AddLine draws the passed line to the back-buffer of the render context
- // and copies it into a texture afterwards (offscreen rendering).
- // This leaves an in invalidated screen. Calling Draw() makes sure,
- // that the back-buffer stores the sing-screen, when the next
- // swap between the back- and front-buffer is done (eliminates flickering)
- // <note> calling AddLine() right before the regular screen update (Display.Draw)
- // would be a better solution.</note>
- Draw;
end;
function TLyricsSyncSource.GetClock(): real;