unit ULyrics;
interface
{$IFDEF FPC}
{$MODE Delphi}
{$ENDIF}
{$I switches.inc}
uses
gl,
glext,
UTexture,
UThemes,
UMusic;
type
// stores two textures for enabled/disabled states
TPlayerIconTex = array [0..1] of TTexture;
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?
end;
ALyricWord = 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?
constructor Create();
destructor Destroy(); override;
procedure Reset();
end;
TLyricEngine = class
private
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)
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
// 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);
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
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
// 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
{ // 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
}
UseLinearFilter: Boolean; //Should Linear Tex Filter be used
// song specific settings
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)
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
// clears all cached song specific information
procedure Clear(cBPM: Real = 0; cResolution: Integer = 0);
function GetUpperLine(): TLyricLine;
function GetLowerLine(): TLyricLine;
function GetUpperLineIndex(): Integer;
constructor Create; overload;
constructor Create(ULX,ULY,ULW,ULS,LLX,LLY,LLW,LLS: Real); overload;
procedure LoadTextures;
destructor Destroy; override;
end;
implementation
uses SysUtils,
USkins,
TextGL,
UGraphic,
UDisplay,
math,
UIni;
//-----------
//Helper procs to use TRGB in Opengl ...maybe this should be somewhere else
//-----------
procedure glColorRGB(Color: TRGB); overload;
begin
glColor3f(Color.R, Color.G, Color.B);
end;
procedure glColorRGB(Color: TRGB; Alpha: Real); overload;
begin
glColor4f(Color.R, Color.G, Color.B, Alpha);
end;
procedure glColorRGB(Color: TRGBA); overload;
begin
glColor4f(Color.R, Color.G, Color.B, Color.A);
end;
procedure glColorRGB(Color: TRGBA; Alpha: Real); overload;
begin
glColor4f(Color.R, Color.G, Color.B, Min(Color.A, Alpha));
end;
{ TLyricLine }
constructor TLyricLine.Create();
begin
inherited;
Reset();
end;
destructor TLyricLine.Destroy();
begin
SetLength(Words, 0);
inherited;
end;
procedure TLyricLine.Reset();
begin
Start := 0;
StartNote := 0;
Length := 0;
LastLine := False;
Text := '';
Width := 0;
// duet mode: players of that line (default: all)
Players := $FF;
SetLength(Words, 0);
CurWord := -1;
HasFreestyle := False;
CountFreestyle := 0;
end;
{ TLyricEngine }
//---------------
// Create - Constructor, just get Memory
//---------------
constructor TLyricEngine.Create;
begin
inherited;
BPM := 0;
Resolution := 0;
LCounter := 0;
QueueFull := False;
UpperLine := TLyricLine.Create;
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);
LowerLineX := LLX;
LowerLineW := LLW;
LowerLineY := LLY;
LowerLineSize := Trunc(LLS);
LoadTextures;
end;
//---------------
// Destroy - Frees Memory
//---------------
destructor TLyricEngine.Destroy;
begin
UpperLine.Free;
LowerLine.Free;
QueueLine.Free;
inherited;
end;
//---------------
// Clear - Clears all cached Song specific Information
//---------------
procedure TLyricEngine.Clear(cBPM: Real; cResolution: Integer);
begin
BPM := cBPM;
Resolution := cResolution;
LCounter := 0;
QueueFull := False;
LastDrawBeat:=0;
end;
//---------------
// LoadTextures - Load Player Textures and Create Lyric Textures
//---------------
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);
// ball for current word hover in ball effect
BallTex := Texture.LoadTexture(Skin.GetTextureFileName('Ball'), TEXTURE_TYPE_TRANSPARENT, 0);
// duet mode: load player icon
for I := 0 to 5 do
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.
//---------------
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;
begin
// only add lines, if there is space
if not IsQueueFull then
begin
// set LyricLine to line to write to
if (LineCounter = 0) then
LyricLine := UpperLine
else if (LineCounter = 1) then
LyricLine := LowerLine
else
begin
// now the queue is full
LyricLine := QueueLine;
QueueFull := True;
end;
end
else
begin // rotate lines (round-robin-like)
LyricLine := UpperLine;
UpperLine := LowerLine;
LowerLine := QueueLine;
QueueLine := LyricLine;
end;
// reset line state
LyricLine.Reset();
// check if sentence has notes
if (Line <> nil) and (Length(Line.Note) > 0) then
begin
// copy values from SongLine to LyricLine
LyricLine.Start := Line.Start;
LyricLine.StartNote := Line.Note[0].Start;
LyricLine.Length := Line.Note[High(Line.Note)].Start +
Line.Note[High(Line.Note)].Length -
Line.Note[0].Start;
LyricLine.LastLine := Line.LastLine;
// copy words
SetLength(LyricLine.Words, Length(Line.Note));
for I := 0 to High(Line.Note) do
begin
LyricLine.Words[I].Start := Line.Note[I].Start;
LyricLine.Words[I].Length := Line.Note[I].Length;
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);
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);
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 600-64, 1024, 64, 0);
// 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)
// 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);
begin
DrawLyrics(Beat);
LastDrawBeat := Beat;
end;
//---------------
// DrawLyrics(private) - Helper for Draw; main Drawing procedure
//---------------
procedure TLyricEngine.DrawLyrics(Beat: Real);
begin
DrawLyricsLine(UpperLineX, UpperLineW, UpperlineY, 15, Upperline, Beat);
DrawLyricsLine(LowerLineX, LowerLineW, LowerlineY, 15, Lowerline, Beat);
end;
//---------------
// DrawPlayerIcon(private) - Helper for Draw; Draws a Playericon
//---------------
procedure TLyricEngine.DrawPlayerIcon(Player: Byte; Enabled: Boolean; X, Y, Size, Alpha: Real);
var
IEnabled: Byte;
begin
if Enabled then
IEnabled := 0
else
IEnabled := 1;
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBindTexture(GL_TEXTURE_2D, PlayerIconTex[Player][IEnabled].TexNum);
glColor4f(1, 1, 1, Alpha);
glBegin(GL_QUADS);
glTexCoord2f(0, 0); glVertex2f(X, Y);
glTexCoord2f(0, 1); glVertex2f(X, Y + Size);
glTexCoord2f(1, 1); glVertex2f(X + Size, Y + Size);
glTexCoord2f(1, 0); glVertex2f(X + Size, Y);
glEnd;
glDisable(GL_BLEND);
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);
begin
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBindTexture(GL_TEXTURE_2D, BallTex.TexNum);
glColor4f(1, 1, 1, Alpha);
glBegin(GL_QUADS);
glTexCoord2f(0, 0); glVertex2f(XBall - 10, YBall);
glTexCoord2f(0, 1); glVertex2f(XBall - 10, YBall + 20);
glTexCoord2f(1, 1); glVertex2f(XBall + 10, YBall + 20);
glTexCoord2f(1, 0); glVertex2f(XBall + 10, YBall);
glEnd;
glDisable(GL_BLEND);
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);
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
{// duet mode
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
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);
IconAlpha := Frac(Beat/(Resolution*4));
DrawPlayerIcon (0, True, X, Y + (42 - IconSize) / 2 , IconSize, IconAlpha);
DrawPlayerIcon (1, True, X + IconSize + 1, Y + (42 - IconSize) / 2, IconSize, IconAlpha);
DrawPlayerIcon (2, True, X + (IconSize + 1)*2, Y + (42 - IconSize) / 2, IconSize, IconAlpha);
}
LyricX := X + W/2 - Line.Width/2;
LyricX2 := LyricX + Line.Width;
// maybe center smaller lines
//LyricY := Y;
LyricY := Y + ((Size / Line.Size - 1) * LyricsHeight) / 2;
Alpha := 1;
// check if this line is active (at least its first note must be active)
if (Beat >= Line.StartNote) then
begin
// if this line just got active, CurWord is -1,
// this means we should try to make the first word active
if (Line.CurWord = -1) then
Line.CurWord := 0;
// check if the current active word is still active.
// Otherwise proceed to the next word if there is one in this line.
// Note: the max. value of Line.CurWord is High(Line.Words)
if (Line.CurWord < High(Line.Words)) and
(Beat >= Line.Words[Line.CurWord + 1].Start) then
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];
// calc the progress of the lyrics effect
Progress := (Beat - CurWord.Start) / CurWord.Length;
if Progress >= 1 then
Progress := 1;
if Progress <= 0 then
Progress := 0;
// last word of this line finished, but this line did not hide -> fade out
if Line.LastLine and
(Beat > LastWord.Start + LastWord.Length) then
begin
Alpha := 1 - (Beat - (LastWord.Start + LastWord.Length)) / 15;
if (Alpha < 0) then
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
glColorRGB(LineColor_en, Alpha)
else
glColorRGB(LineColor_act, Alpha);
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
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
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);
glTexCoord2f((CurWordEnd + FreestyleDiff)/1024, 1);
glVertex2f(LyricX + CurWordEnd + FreestyleDiff, LyricY);
glEnd;
if (Ini.LyricsEffect = 4) then
LyricY := LyricY + 8 * (1-Progress);
end
// draw active word:
// type 1: zoom lyric effect
// change color and zoom current word
else if Ini.LyricsEffect = 1 then
begin
glPushMatrix;
glTranslatef(LyricX + CurWordStart + (CurWordEnd-CurWordStart)/2,
LyricY + LyricsHeight/2, 0);
// set current zoom factor
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);
glTexCoord2f(CurWordStart/1024, 1-LyricsHeight/64);
glVertex2f(-(CurWordEnd-CurWordStart)/2, LyricsHeight/2);
glTexCoord2f(CurWordEnd/1024, 1-LyricsHeight/64);
glVertex2f((CurWordEnd-CurWordStart)/2, LyricsHeight/2);
glTexCoord2f((CurWordEnd + FreestyleDiff)/1024, 1);
glVertex2f((CurWordEnd-CurWordStart)/2 + FreestyleDiff, -LyricsHeight/2);
glEnd;
glPopMatrix;
end;
glDisable(GL_TEXTURE_2D);
glDisable(GL_BLEND);
// type 3: ball lyric effect
if Ini.LyricsEffect = 3 then
begin
DrawBall(LyricX + CurWordStart + (CurWordEnd-CurWordStart) * 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);
// enable the upper, disable the lower line
if (Line = UpperLine) then
glColorRGB(LineColor_en)
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);
end;
end;
//---------------
// GetUpperLine() - Returns a reference to the upper line
//---------------
function TLyricEngine.GetUpperLine(): TLyricLine;
begin
Result := UpperLine;
end;
//---------------
// GetLowerLine() - 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;
const
QUEUE_SIZE = 3;
begin
// no line in queue
if (LineCounter <= 0) then
Result := -1
// no line has been removed from queue yet
else if (LineCounter <= QUEUE_SIZE) then
Result := 0
// lines have been removed from queue already
else
Result := LineCounter - QUEUE_SIZE;
end;
end.