aboutsummaryrefslogtreecommitdiffstats
path: root/Game/Code
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--Game/Code/Classes/UDraw.pas46
-rw-r--r--Game/Code/Classes/ULyrics.pas700
-rw-r--r--Game/Code/Classes/UMain.pas77
-rw-r--r--Game/Code/Classes/UMusic.pas81
-rw-r--r--Game/Code/Screens/UScreenSing.pas112
-rw-r--r--Game/Code/Screens/UScreenSingModi.pas11
6 files changed, 568 insertions, 459 deletions
diff --git a/Game/Code/Classes/UDraw.pas b/Game/Code/Classes/UDraw.pas
index 24a52264..a5bd8afa 100644
--- a/Game/Code/Classes/UDraw.pas
+++ b/Game/Code/Classes/UDraw.pas
@@ -434,7 +434,7 @@ begin
// Perfect note is stored
if Perfect and (Ini.EffectSing=1) then
begin
- A := 1 - 2*(LineState.CurrentTime - GetTimeFromBeat(Start+Length));
+ A := 1 - 2*(LineState.GetCurrentTime() - GetTimeFromBeat(Start+Length));
if not (Start+Length-1 = LineState.CurrentBeatD) then
begin
//Star animation counter
@@ -1336,13 +1336,15 @@ begin
end;
procedure SingDrawTimeBar();
-var x,y: real;
- width, height: real;
- lTmp : real;
+var
+ x,y: real;
+ width, height: real;
+ LyricsProgress: real;
+ CurLyricsTime: real;
begin
x := Theme.Sing.StaticTimeProgress.x;
y := Theme.Sing.StaticTimeProgress.y;
-
+
width := Theme.Sing.StaticTimeProgress.w;
height := Theme.Sing.StaticTimeProgress.h;
@@ -1356,30 +1358,28 @@ begin
glBindTexture(GL_TEXTURE_2D, Tex_TimeProgress.TexNum);
glBegin(GL_QUADS);
- try
glTexCoord2f(0, 0);
- glVertex2f(x,y);
-
- if ( LineState.CurrentTime > 0 ) AND
- ( LineState.TotalTime > 0 ) THEN
- BEGIN
- lTmp := LineState.CurrentTime/LineState.TotalTime;
- glTexCoord2f((width*LineState.CurrentTime/LineState.TotalTime)/8, 0);
- glVertex2f(x+width*LineState.CurrentTime/LineState.TotalTime, y);
-
- glTexCoord2f((width*LineState.CurrentTime/LineState.TotalTime)/8, 1);
- glVertex2f(x+width*LineState.CurrentTime/LineState.TotalTime, y+height);
- END;
+ glVertex2f(x, y);
+
+ CurLyricsTime := LineState.GetCurrentTime();
+ if (CurLyricsTime > 0) and
+ (LineState.TotalTime > 0) then
+ begin
+ LyricsProgress := CurLyricsTime / LineState.TotalTime;
+ glTexCoord2f((width * LyricsProgress) / 8, 0);
+ glVertex2f(x + width * LyricsProgress, y);
+
+ glTexCoord2f((width * LyricsProgress) / 8, 1);
+ glVertex2f(x + width * LyricsProgress, y + height);
+ end;
glTexCoord2f(0, 1);
- glVertex2f(x, y+height);
- finally
- glEnd;
- end;
+ glVertex2f(x, y + height);
+ glEnd;
glDisable(GL_TEXTURE_2D);
glDisable(GL_BLEND);
- glcolor4f(1,1,1,1);
+ glcolor4f(1, 1, 1, 1);
end;
end.
diff --git a/Game/Code/Classes/ULyrics.pas b/Game/Code/Classes/ULyrics.pas
index e5427773..eced2991 100644
--- a/Game/Code/Classes/ULyrics.pas
+++ b/Game/Code/Classes/ULyrics.pas
@@ -16,6 +16,10 @@ uses
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
@@ -34,34 +38,35 @@ type
Size: Byte; // fontsize
Words: ALyricWord; // words in this line
CurWord: Integer; // current active word idx (only valid if line is active)
- Start: Cardinal; // start of this line in quarters
- Length: Cardinal; // length in quarters
+ 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)
- Done: Boolean; // is sentence already sung?
- LastLine: Boolean; // is this the last line ob the song?
+ LastLine: Boolean; // is this the last line of the song?
+
+ constructor Create();
+ destructor Destroy(); override;
+ procedure Reset();
end;
TLyricEngine = class
private
- EoLastSentence: Real; // end of the previous sentence (in beats)
- LastDrawBeat: Real;
+ LastDrawBeat: Real;
UpperLine: TLyricLine; // first line displayed (top)
LowerLine: TLyricLine; // second lind displayed (bottom)
- QueueLine: TLyricLine; // third line (queue and will be displayed when next line is finished)
+ 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
+ IndicatorTex: TTexture; // texture for lyric indikator
+ BallTex: TTexture; // texture of the ball for the lyric effect
- inQueue: Boolean; // is line in queue
- 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: Word; // line counter
// duet mode - textures for player icons
- PlayerIconTex: array[0..5] of // player idx
- array [0..1] of // enabled disabled
- TTexture;
-
+ // 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);
@@ -93,14 +98,14 @@ type
FadeOutEffect: Byte; //Effect for Line Fading out: 0: No Effect; 1: Fade Effect; 2: Move Upwards
}
- UseLinearFilter:Boolean; //Should Linear Tex Filter be used
+ UseLinearFilter: Boolean; //Should Linear Tex Filter be used
// song specific settings
BPM: Real;
Resolution: Integer;
// properties to easily read options of this class
- property LineinQueue: Boolean read inQueue; // line in queue?
+ 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
@@ -112,6 +117,8 @@ type
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;
@@ -151,7 +158,42 @@ 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
@@ -163,18 +205,14 @@ begin
BPM := 0;
Resolution := 0;
LCounter := 0;
- inQueue := False;
+ QueueFull := False;
UpperLine := TLyricLine.Create;
LowerLine := TLyricLine.Create;
QueueLine := TLyricLine.Create;
- UpperLine.Done := True;
- LowerLine.Done := True;
- QueueLine.Done := True;
-
UseLinearFilter := True;
- LastDrawBeat:=0;
+ LastDrawBeat := 0;
end;
constructor TLyricEngine.Create(ULX,ULY,ULW,ULS,LLX,LLY,LLW,LLS:Real);
@@ -214,12 +252,8 @@ begin
BPM := cBPM;
Resolution := cResolution;
LCounter := 0;
- inQueue := False;
+ QueueFull := False;
- UpperLine.Done := True;
- LowerLine.Done := True;
- QueueLine.Done := True;
-
LastDrawBeat:=0;
end;
@@ -230,37 +264,25 @@ end;
procedure TLyricEngine.LoadTextures;
var
I: Integer;
-
+
function CreateLineTex: glUint;
- var
- PTexData: Pointer;
begin
- try
- // get memory
- GetMem(pTexData, 1024*64*4);
-
- // generate and bind Texture
- glGenTextures(1, @Result);
- glBindTexture(GL_TEXTURE_2D, Result);
+ // generate and bind Texture
+ glGenTextures(1, @Result);
+ glBindTexture(GL_TEXTURE_2D, Result);
- // get texture memeory
- glTexImage2D(GL_TEXTURE_2D, 0, 4, 1024, 64, 0, GL_RGBA, GL_UNSIGNED_BYTE, pTexData);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ 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;
-
- finally
- // free unused memory
- FreeMem(pTexData);
+ 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);
@@ -268,12 +290,12 @@ begin
BallTex := Texture.LoadTexture(Skin.GetTextureFileName('Ball'), TEXTURE_TYPE_TRANSPARENT, 0);
// duet mode: load player icon
- For I := 0 to 5 do
+ 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;
@@ -283,15 +305,20 @@ 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);
+procedure TLyricEngine.AddLine(Line: PLine);
var
LyricLine: TLyricLine;
- countNotes: Cardinal;
- Viewport: Array[0..3] of Integer;
-
PosX: Real;
- I: Integer;
+ I: Integer;
+ CurWord: PLyricWord;
+ RenderPass: Integer;
function CalcWidth(LyricLine: TLyricLine): Real;
begin
@@ -306,20 +333,18 @@ var
begin
// only add lines, if there is space
- If not LineinQueue then
+ if not IsQueueFull then
begin
- // set pointer to line to write
-
- If (LineCounter = 0) then
+ // 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;
-
- //now there is a queued line
- inQueue := True;
+ QueueFull := True;
end;
end
else
@@ -330,48 +355,38 @@ begin
QueueLine := LyricLine;
end;
- // sentence has notes?
- If Line = nil then
- begin
- // reset all values, if the new line is nil (lines after the last line)
- LyricLine.Start := 0;
- LyricLine.Length := 0;
- LyricLine.CurWord := -1;
- LyricLine.LastLine := False;
- LyricLine.Width := 0;
- SetLength(LyricLine.Words, 0);
- end
- else if Length(Line.Note) > 0 then
+ // 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
- CountNotes := High(Line.Note);
- LyricLine.Start := Line.Note[0].Start;
- LyricLine.Length := Line.Note[CountNotes].Start + Line.Note[CountNotes].Length - LyricLine.Start;
- LyricLine.CurWord := -1;
+ 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;
- // default values - set later
- LyricLine.HasFreestyle := False;
- LyricLine.CountFreestyle := 0;
- LyricLine.Text := '';
-
- // duet mode: players of that line
- LyricLine.Players := 127;
-
- //copy words
- SetLength(LyricLine.Words, CountNotes + 1);
- For I := 0 to CountNotes do
+ // 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.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
+ 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
@@ -380,6 +395,7 @@ begin
LyricLine.Size := UpperLineSize;
SetFontSize(LyricLine.Size);
SetFontItalic(False);
+ SetFontReflection(False, 0);
glColor4f(1, 1, 1, 1);
// change fontsize to fit the screen
@@ -395,61 +411,95 @@ begin
LyricLine.Width := CalcWidth(LyricLine);
end;
- // create LyricTexture - prepare OpenGL
- glGetIntegerv(GL_VIEWPORT, @ViewPort);
- glClearColor(0.0,0.0,0.0,0.0);
+ // 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);
- glViewPort(0,0,800,600);
- // set word positions and line size
- PosX := 0;
- for I := 0 to High(LyricLine.Words) do
+ 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
- with LyricLine.Words[I] do
+ 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
- SetFontItalic(Freestyle);
+ // second pass: now we will blend overlapping characters.
+ SetFontBlend(true);
+ glDisable(GL_DEPTH_TEST);
+ end;
- X := PosX;
+ 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];
- //Draw Lyrics
+ SetFontItalic(CurWord.Freestyle);
+
+ CurWord.X := PosX;
+
+ // Draw Lyrics
SetFontPos(PosX, 0);
- glPrint(PChar(Text));
-
- Width := glTextWidth(PChar(Text));
- if (I < High(LyricLine.Words)) AND Freestyle AND not LyricLine.Words[I+1].Freestyle then
- Width := Width + 10
- else
- if (I = High(LyricLine.Words)) AND Freestyle then
- Width := Width + 12;
- PosX := PosX + Width;
+ 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;
- end
- else
- begin
- // create LyricTexture - prepare OpenGL
- glGetIntegerv(GL_VIEWPORT, @ViewPort);
- glClearColor(0.0,0.0,0.0,0.0);
- glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
- glViewPort(0,0,800,600);
- end;
-
- //for debugging, is this used anymore?
- //Display.ScreenShot;
-
- //Copy to Texture
- glEnable(GL_ALPHA);
- glBindTexture(GL_TEXTURE_2D, LyricLine.Tex);
- glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 600-64, 1024, 64, 0);
- glDisable(GL_ALPHA);
+
+ // copy back-buffer to texture
+ glBindTexture(GL_TEXTURE_2D, LyricLine.Tex);
+ glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 600-64, 1024, 64, 0);
- //Clear Buffer
- glClearColor(0,0,0,0);
- glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
+ // restore OpenGL state
+ glPopAttrib();
- glViewPort(ViewPort[0], ViewPort[1], ViewPort[2], ViewPort[3]);
+ // 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
+ // increase the counter
Inc(LCounter);
end;
@@ -458,7 +508,7 @@ 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);
+procedure TLyricEngine.Draw(Beat: Real);
begin
DrawLyrics(Beat);
LastDrawBeat := Beat;
@@ -467,7 +517,7 @@ end;
//---------------
// DrawLyrics(private) - Helper for Draw; main Drawing procedure
//---------------
-procedure TLyricEngine.DrawLyrics (Beat: Real);
+procedure TLyricEngine.DrawLyrics(Beat: Real);
begin
DrawLyricsLine(UpperLineX, UpperLineW, UpperlineY, 15, Upperline, Beat);
DrawLyricsLine(LowerLineX, LowerLineW, LowerlineY, 15, Lowerline, Beat);
@@ -477,31 +527,29 @@ end;
// DrawPlayerIcon(private) - Helper for Draw; Draws a Playericon
//---------------
procedure TLyricEngine.DrawPlayerIcon(Player: Byte; Enabled: Boolean; X, Y, Size, Alpha: Real);
-var IEnabled: Byte;
+var
+ IEnabled: Byte;
begin
case Enabled of
- True: IEnabled := 0;
+ True: IEnabled := 0;
False: IEnabled := 1;
end;
- try
- 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;
-
- finally
- glDisable(GL_BLEND);
- glDisable(GL_TEXTURE_2D);
- end;
+ 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;
//---------------
@@ -509,24 +557,21 @@ end;
//---------------
procedure TLyricEngine.DrawBall(XBall, YBall, Alpha: Real);
begin
- try
- 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;
-
- finally
- glDisable(GL_BLEND);
- glDisable(GL_TEXTURE_2D);
- end;
+ 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;
//---------------
@@ -542,31 +587,36 @@ var
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
- // lines with a width lower than 0, have not to be draw
- if Line.Width <= 0 then
- exit;
+ // 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);
-
+ 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;
+
+ LyricX := X + W/2 - Line.Width/2;
LyricX2 := LyricX + Line.Width;
// maybe center smaller lines
@@ -575,70 +625,70 @@ begin
Alpha := 1;
- // word in the sentence is active?
- if (Line.Start < Beat) then
+ // 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, then CurWord is still -1
- // this means, we should try to make the first word active
- // then we check if the current active word is still meant to be active
- // if not, we proceed to the next word
- if Line.CurWord = -1 then
- Line.CurWord:=0;
-
- if (Line.CurWord < High(Line.Words)) and (Beat >= (Line.Words[Line.CurWord + 1].Start)) then
- Line.CurWord:=Line.CurWord+1;
+ // 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;
- // last word of this line finished, but this line did not hide
- if (Line.CurWord > High(Line.Words)) then
+ // 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 := Line.Words[High(Line.Words)].X + Line.Words[High(Line.Words)].Width;
+ CurWordStart := CurWordStart + CurWord.Width * Progress;
CurWordEnd := CurWordStart;
+ end;
- // fade out last line
- if Line.LastLine then
- begin
- Alpha := 1 - (Beat - (Line.Words[High(Line.Words)].Start + Line.Words[High(Line.Words)].Length)) / 15;
- if (Alpha < 0) then
- Alpha := 0;
- end;
- end
- else
+ if CurWord.Freestyle then
begin
- with Line.Words[Line.CurWord] do
+ if (Line.CurWord < High(Line.Words)) and
+ (not Line.Words[Line.CurWord + 1].Freestyle) then
begin
- Progress := (Beat - Start) / Length;
- if Progress >= 1 then
- Progress := 1;
-
- if Progress <= 0 then
- Progress := 0;
-
- CurWordStart:=X;
- CurWordEnd:=X+Width;
-
- // Slide Effect
- // simply paint the active texture to the current position
- if Ini.LyricsEffect = 2 then
- begin
- CurWordStart := CurWordStart + Width * progress;
- CurWordEnd := CurWordStart;
- end;
-
- if (Line.CurWord < High(Line.Words)) and
- Freestyle and
- (not Line.Words[Line.CurWord + 1].Freestyle) then
- begin
- FreestyleDiff := 2;
- end
- else
- if Freestyle then
- begin
- FreestyleDiff := 12;
- CurWordStart := CurWordStart - 1;
- CurWordEnd := CurWordEnd - 2;
- end;
+ FreestyleDiff := 2;
+ end
+ else
+ begin
+ FreestyleDiff := 12;
+ CurWordStart := CurWordStart - 1;
+ CurWordEnd := CurWordEnd - 2;
end;
end;
@@ -648,26 +698,43 @@ begin
glBindTexture(GL_TEXTURE_2D, Line.Tex);
// draw sentence up to current word
- if (Ini.LyricsEffect = 3) or (Ini.LyricsEffect = 4) then
+ // 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);
+ 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);
+ 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);
+ 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:
@@ -675,78 +742,99 @@ begin
// type 3: ball lyric effect
// type 4: shift lyric effect
// only change the color of the current word
- if (Ini.LyricsEffect = 0) or (Ini.LyricsEffect = 3) or (Ini.LyricsEffect = 4) then
+ if (Ini.LyricsEffect in [0, 3, 4]) then
begin
- { // maybe fade in?
- glColor4f(LineColor_en.r,LineColor_en.g,LineColor_en.b,1-progress);
- glBegin(GL_QUADS);
- glTexCoord2f(CurWordStart/1024, 1); glVertex2f(LyricX+CurWordStart, Y);
- glTexCoord2f(CurWordStart/1024, 0); glVertex2f(LyricX+CurWordStart, Y + 64);
- glTexCoord2f(CurWordEnd/1024, 0); glVertex2f(LyricX+CurWordEnd, Y + 64);
- glTexCoord2f(CurWordEnd/1024, 1); glVertex2f(LyricX+CurWordEnd, Y);
- glEnd;
- }
-
if (Ini.LyricsEffect = 4) then
- LyricY := LyricY - 8 * (1-progress);
-
- glColor3f(LineColor_act.r,LineColor_act.g,LineColor_act.b);
+ 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);
+ 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);
+ 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);
- glScalef(1.0+(1-progress)/2,1.0+(1-progress)/2,1.0);
- glColor4f(LineColor_en.r,LineColor_en.g,LineColor_en.b,1-progress);
- 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;
- glColor4f(LineColor_act.r,LineColor_act.g,LineColor_act.b,1);
+
+ 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);
+ 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
- DrawBall(LyricX + CurWordStart + (CurWordEnd - CurWordStart) * progress, LyricY - 15 - 15*sin(progress * pi), Alpha);
+ begin
+ DrawBall(LyricX + CurWordStart + (CurWordEnd-CurWordStart) * Progress,
+ LyricY - 15 - 15*sin(Progress * Pi), Alpha);
+ end;
end
else
begin
- // draw complete inactive sentence if line hasn't started but is already shown
+ // 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);
- glColorRGB(LineColor_dis);
+ // 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);
+ 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);
@@ -754,15 +842,39 @@ begin
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.
diff --git a/Game/Code/Classes/UMain.pas b/Game/Code/Classes/UMain.pas
index 95c1914a..a9add539 100644
--- a/Game/Code/Classes/UMain.pas
+++ b/Game/Code/Classes/UMain.pas
@@ -671,29 +671,13 @@ var
CP: integer;
Done: real;
N: integer;
+ CurLine: PLine;
+ CurNote: PLineFragment;
begin
- LineState.OldBeat := LineState.CurrentBeat;
- // new system with variable BPM in function
- LineState.MidBeat := GetMidBeat(LineState.CurrentTime -
- (CurrentSong.Gap {+ 90 I've forgotten for what it is}) / 1000);
- LineState.CurrentBeat := Floor(LineState.MidBeat);
-
- //LineState.OldHalf := LineState.AktHalf;
- //LineState.MidHalf := LineState.MidBeat + 0.5;
- //LineState.AktHalf := Floor(LineState.MidHalf);
-
- LineState.OldBeatC := LineState.CurrentBeatC;
- LineState.MidBeatC := GetMidBeat(LineState.CurrentTime - (CurrentSong.Gap) / 1000);
- LineState.CurrentBeatC := Floor(LineState.MidBeatC);
-
- LineState.OldBeatD := LineState.CurrentBeatD;
- // MidBeat with additional GAP
- LineState.MidBeatD := -0.5+GetMidBeat(LineState.CurrentTime - (CurrentSong.Gap + 120 + 20) / 1000);
- LineState.CurrentBeatD := Floor(LineState.MidBeatD);
- LineState.FracBeatD := Frac(LineState.MidBeatD);
+ LineState.UpdateBeats();
// sentences routines
- for CountGr := 0 to 0 do //High(Gracz)
+ for CountGr := 0 to 0 do //High(Lines)
begin;
CP := CountGr;
// old parts
@@ -713,10 +697,6 @@ begin
end; // for CountGr
- // execute operations on beat
- if (LineState.CurrentBeat >= 0) and (LineState.OldBeat <> LineState.CurrentBeat) then
- NewBeat(Screen);
-
// make some operations on clicks
if {(LineState.CurrentBeatC >= 0) and }(LineState.OldBeatC <> LineState.CurrentBeatC) then
NewBeatClick(Screen);
@@ -725,42 +705,19 @@ begin
if (LineState.CurrentBeatD >= 0) and (LineState.OldBeatD <> LineState.CurrentBeatD) then
NewBeatDetect(Screen);
- // execute operations on beat field
-// if (LineState.AktHalf >= 1) and (LineState.OldHalf <> LineState.AktHalf) then
-// NewHalf;
+ CurLine := @Lines[0].Line[Lines[0].Current];
// remove moving text
Done := 1;
- for N := 0 to Lines[0].Line[Lines[0].Current].HighNote do
+ for N := 0 to CurLine.HighNote do
begin
- if (Lines[0].Line[Lines[0].Current].Note[N].Start <= LineState.MidBeat) and
- (Lines[0].Line[Lines[0].Current].Note[N].Start + Lines[0].Line[Lines[0].Current].Note[N].Length >= LineState.MidBeat) then
+ CurNote := @CurLine.Note[N];
+ if (CurNote.Start <= LineState.MidBeat) and
+ (CurNote.Start + CurNote.Length >= LineState.MidBeat) then
begin
- Done := (LineState.MidBeat - Lines[0].Line[Lines[0].Current].Note[N].Start) / (Lines[0].Line[Lines[0].Current].Note[N].Length);
- end;
- end;
-
- N := Lines[0].Line[Lines[0].Current].HighNote;
-
- // if last note is used
- {// todo: Lyrics
- if (Ini.LyricsEffect = 1) and (Done = 1) and
- (LineState.MidBeat > Lines[0].Line[Lines[0].Current].Note[N].Start + Lines[0].Line[Lines[0].Current].Note[N].Length)
- then Screen.LyricMain.Selected := -1;
-
- if Done > 1 then Done := 1;
- Screen.LyricMain.Done := Done;
- }
-
- // use Done with LCD
- {
- with ScreenSing do begin
- if LyricMain.Selected >= 0 then begin
- LCD.MoveCursor(1, LyricMain.SelectedLetter + Round((LyricMain.SelectedLength-1) * Done));
- LCD.ShowCursor;
+ Done := (LineState.MidBeat - CurNote.Start) / CurNote.Length;
end;
end;
- }
end;
procedure NewSentence(Screen: TScreenSing);
@@ -775,20 +732,6 @@ begin
SetLength(Player[i].Note, 0);
end;
- // add words to lyrics
- with Screen do
- begin
- {LyricMain.AddCzesc(Lines[0].Current);
- if Lines[0].Current < Lines[0].High then
- LyricSub.AddCzesc(Lines[0].Current+1)
- else
- LyricSub.Clear;}
- while (not Lyrics.LineinQueue) and (Lyrics.LineCounter <= High(Lines[0].Line)) do
- Lyrics.AddLine(@Lines[0].Line[Lyrics.LineCounter]);
- end;
-
- //Screen.UpdateLCD;
-
// on sentence change...
Screen.onSentenceChange(Lines[0].Current);
end;
diff --git a/Game/Code/Classes/UMusic.pas b/Game/Code/Classes/UMusic.pas
index 3ef6aca2..2df5286e 100644
--- a/Game/Code/Classes/UMusic.pas
+++ b/Game/Code/Classes/UMusic.pas
@@ -65,46 +65,47 @@ type
end;
(**
- * TLineState contains all information that concerns the current frames
+ * TLineState contains all information that concerns the
+ * state of the lyrics.
*)
TLineState = class
private
Timer: TRelativeTimer; // keeps track of the current time
-
- function GetCurrentTime(): real;
- procedure SetCurrentTime(Time: real);
public
OldBeat: integer; // previous discovered beat
- CurrentBeat: integer;
- MidBeat: real; // like CurrentBeat
+ CurrentBeat: integer; // current beat (rounded)
+ MidBeat: real; // current beat (float)
// now we use this for super synchronization!
// only used when analyzing voice
// TODO: change ...D to ...Detect(ed)
OldBeatD: integer; // previous discovered beat
- CurrentBeatD: integer;
- MidBeatD: real; // like CurrentBeatD
- FracBeatD: real; // fractional part of MidBeatD
+ CurrentBeatD: integer; // current discovered beat (rounded)
+ MidBeatD: real; // current discovered beat (float)
// we use this for audible clicks
// TODO: Change ...C to ...Click
OldBeatC: integer; // previous discovered beat
CurrentBeatC: integer;
MidBeatC: real; // like CurrentBeatC
- FracBeatC: real; // fractional part of MidBeatC
OldLine: integer; // previous displayed sentence
+ StartTime: real; // time till start of lyrics (= Gap)
TotalTime: real; // total song time
constructor Create();
procedure Pause();
procedure Resume();
+ procedure Reset();
+ procedure UpdateBeats();
+
(**
- * current song time used as base-timer for lyrics etc.
+ * current song time (in seconds) used as base-timer for lyrics etc.
*)
- property CurrentTime: real read GetCurrentTime write SetCurrentTime;
+ function GetCurrentTime(): real;
+ procedure SetCurrentTime(Time: real);
end;
@@ -926,11 +927,17 @@ begin
end;
end;
+
+{ TVoiceRemoval }
+
constructor TLineState.Create();
begin
// create a triggered timer, so we can Pause() it, set the time
// and Resume() it afterwards for better synching.
Timer := TRelativeTimer.Create(true);
+
+ // reset state
+ Reset();
end;
procedure TLineState.Pause();
@@ -955,6 +962,56 @@ begin
Result := Timer.GetTime();
end;
+(**
+ * Resets the timer and state of the lyrics.
+ * The timer will be stopped afterwards so you have to call Resume()
+ * to start the lyrics timer.
+ *)
+procedure TLineState.Reset();
+begin
+ Pause();
+ SetCurrentTime(0);
+
+ StartTime := 0;
+ TotalTime := 0;
+
+ OldBeat := -1;
+ MidBeat := -1;
+ CurrentBeat := -1;
+
+ OldBeatC := -1;
+ MidBeatC := -1;
+ CurrentBeatC := -1;
+
+ OldBeatD := -1;
+ MidBeatD := -1;
+ CurrentBeatD := -1;
+end;
+
+(**
+ * Updates the beat information (CurrentBeat/MidBeat/...) according to the
+ * current lyric time.
+ *)
+procedure TLineState.UpdateBeats();
+var
+ CurLyricsTime: real;
+begin
+ CurLyricsTime := GetCurrentTime();
+
+ OldBeat := CurrentBeat;
+ MidBeat := GetMidBeat(CurLyricsTime - StartTime / 1000);
+ CurrentBeat := Floor(MidBeat);
+
+ OldBeatC := CurrentBeatC;
+ MidBeatC := GetMidBeat(CurLyricsTime - StartTime / 1000);
+ CurrentBeatC := Floor(MidBeatC);
+
+ OldBeatD := CurrentBeatD;
+ // MidBeatD = MidBeat with additional GAP
+ MidBeatD := -0.5 + GetMidBeat(CurLyricsTime - (StartTime + 120 + 20) / 1000);
+ CurrentBeatD := Floor(MidBeatD);
+end;
+
{ TAudioConverter }
diff --git a/Game/Code/Screens/UScreenSing.pas b/Game/Code/Screens/UScreenSing.pas
index 6dcec3cb..a78c7ce0 100644
--- a/Game/Code/Screens/UScreenSing.pas
+++ b/Game/Code/Screens/UScreenSing.pas
@@ -23,7 +23,6 @@ uses UMenu,
TextGL,
gl,
UThemes,
- //ULCD, //TODO: maybe LCD Support as Plugin?
UGraphicClasses,
USingScores;
@@ -113,11 +112,10 @@ type
function Draw: boolean; override;
procedure Finish; virtual;
- //procedure UpdateLCD; //TODO: maybe LCD Support as Plugin?
- procedure Pause; //Pause Mod(Toggles Pause)
+ procedure Pause; // Toggle Pause
- procedure OnSentenceEnd(SentenceIndex: Cardinal); //OnSentenceEnd for LineBonus + Singbar
- procedure OnSentenceChange(SentenceIndex: Cardinal); //OnSentenceChange (for Golden Notes)
+ procedure OnSentenceEnd(SentenceIndex: Cardinal); // for LineBonus + Singbar
+ procedure OnSentenceChange(SentenceIndex: Cardinal); // for Golden Notes
end;
implementation
@@ -495,7 +493,6 @@ begin
fShowVisualization := false;
if (CurrentSong.Video <> '') and FileExists(CurrentSong.Path + CurrentSong.Video) then
begin
- // TODO: use VideoGap and start time
fCurrentVideoPlaybackEngine.Open( CurrentSong.Path + CurrentSong.Video );
fCurrentVideoPlaybackEngine.Position := CurrentSong.VideoGAP + CurrentSong.Start;
CurrentSong.VideoLoaded := true;
@@ -506,16 +503,21 @@ begin
try
Tex_Background := Texture.LoadTexture(CurrentSong.Path + CurrentSong.Background);
except
- log.LogError('Background could not be loaded: ' + CurrentSong.Path + CurrentSong.Background);
+ Log.LogError('Background could not be loaded: ' + CurrentSong.Path + CurrentSong.Background);
Tex_Background.TexNum := 0;
end
else
Tex_Background.TexNum := 0;
// prepare lyrics timer
- LineState.Pause();
- LineState.CurrentTime := CurrentSong.Start;
- LineState.TotalTime := AudioPlayback.Length;
+ LineState.Reset();
+ LineState.SetCurrentTime(CurrentSong.Start);
+ LineState.StartTime := CurrentSong.Gap;
+ if (CurrentSong.Finish > 0) then
+ LineState.TotalTime := CurrentSong.Finish / 1000
+ else
+ LineState.TotalTime := AudioPlayback.Length;
+ LineState.UpdateBeats();
// prepare music
AudioPlayback.Stop();
@@ -526,9 +528,6 @@ begin
// prepare and start voice-capture
AudioInput.CaptureStart;
- if (CurrentSong.Finish > 0) then
- LineState.TotalTime := CurrentSong.Finish / 1000;
- LineState.OldBeat := -1;
for P := 0 to High(Player) do
ClearScores(P);
@@ -654,11 +653,12 @@ begin
end;
end; // case
- // Add lines to lyrics
- while (not Lyrics.LineinQueue) and (Lyrics.LineCounter <= High(Lines[0].Line)) do
+ // Initialize lyrics by filling its queue
+ while (not Lyrics.IsQueueFull) and
+ (Lyrics.LineCounter <= High(Lines[0].Line)) do
+ begin
Lyrics.AddLine(@Lines[0].Line[Lyrics.LineCounter]);
-
- //UpdateLCD; //TODO: maybe LCD Support as Plugin?
+ end;
// Deactivate pause
Paused := False;
@@ -928,6 +928,7 @@ var
Flash: real;
S: integer;
T: integer;
+ CurLyricsTime: real;
begin
// ScoreBG Mod
// TODO: remove this commented out section as we do not need it anymore.
@@ -1064,9 +1065,15 @@ begin
for T := 0 to 1 do
Text[T].X := Text[T].X + 10*ScreenX;
+
+
+ // retrieve current lyrics time, we have to store the value to avoid
+ // that min- and sec-values do not match
+ CurLyricsTime := LineState.GetCurrentTime();
+ Min := Round(CurLyricsTime) div 60;
+ Sec := Round(CurLyricsTime) mod 60;
+
// update static menu with time ...
- Min := Round(LineState.CurrentTime) div 60;
- Sec := Round(LineState.CurrentTime) mod 60;
Text[TextTimeText].Text := '';
if Min < 10 then Text[TextTimeText].Text := '0';
Text[TextTimeText].Text := Text[TextTimeText].Text + IntToStr(Min) + ':';
@@ -1167,7 +1174,7 @@ begin
begin
if assigned( fCurrentVideoPlaybackEngine ) then
begin
- fCurrentVideoPlaybackEngine.GetFrame(LineState.CurrentTime);
+ fCurrentVideoPlaybackEngine.GetFrame(LineState.GetCurrentTime());
fCurrentVideoPlaybackEngine.DrawGL(ScreenAct);
end;
end;
@@ -1180,7 +1187,7 @@ begin
if ShowFinish then
begin
if (not AudioPlayback.Finished) and
- ((CurrentSong.Finish = 0) or (LineState.CurrentTime*1000 <= CurrentSong.Finish)) then
+ ((CurrentSong.Finish = 0) or (LineState.GetCurrentTime()*1000 <= CurrentSong.Finish)) then
begin
// analyze song if not paused
if (not Paused) then
@@ -1270,25 +1277,6 @@ begin
SetFontItalic (False);
end;
-(*
-procedure TScreenSing.UpdateLCD; //TODO: maybe LCD Support as Plugin?
-var
- T: string;
-begin
- //Todo: Lyrics
-{ LCD.HideCursor;
- LCD.Clear;
-
- T := LyricMain.Text;
- if Copy(T, Length(T), 1) <> ' ' then T := T + ' ';
- LCD.AddTextBR(T);
-
- T := LyricSub.Text;
- if Copy(T, Length(T), 1) <> ' ' then T := T + ' ';
- LCD.AddTextBR(T);}
-end;
-*)
-
procedure TScreenSing.OnSentenceEnd(SentenceIndex: Cardinal);
var
PlayerIndex: Integer;
@@ -1374,34 +1362,40 @@ begin
GoldenRec.SpawnPerfectLineTwinkle;
end;
-//Called on Sentence Change S= New Current Sentence
+// Called on sentence change
+// SentenceIndex: index of the new active sentence
procedure TScreenSing.OnSentenceChange(SentenceIndex: Cardinal);
+var
+ LyricEngine: TLyricEngine;
begin
- //GoldenStarsTwinkle Mod
+ //GoldenStarsTwinkle
GoldenRec.SentenceChange;
- if (Lyrics.LineCounter <= High(Lines[0].Line)) then
+
+ // Fill lyrics queue and set upper line to the current sentence
+ while (Lyrics.GetUpperLineIndex() < SentenceIndex) or
+ (not Lyrics.IsQueueFull) do
begin
- Lyrics.AddLine(@Lines[0].Line[Lyrics.LineCounter]);
- end
- else
- Lyrics.AddLine(nil);
-
- // addline uses display memory
- // calling draw makes sure, there's the singscreen in it, when the next
- // swap between onscreen and offscreen buffers is done
- // (this eliminates the onSentenceChange flickering)
- // note: maybe it would be better to make sure, a display redraw is done
- // right after the sentence change (before buffer swap) or make sure
- // onsentencechange is only called right before calling Display.Draw
- // (or whatever it was called)
- Draw;
+ // Add the next line to the queue or a dummy if no more lines are available
+ if (Lyrics.LineCounter <= High(Lines[0].Line)) then
+ Lyrics.AddLine(@Lines[0].Line[Lyrics.LineCounter])
+ else
+ Lyrics.AddLine(nil);
+ end;
- //GoldenStarsTwinkle Mod 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.
+ Draw;
end;
function TLyricsSyncSource.GetClock(): real;
begin
- Result := LineState.CurrentTime;
+ Result := LineState.GetCurrentTime();
end;
end.
diff --git a/Game/Code/Screens/UScreenSingModi.pas b/Game/Code/Screens/UScreenSingModi.pas
index 7606abe6..3eeb0374 100644
--- a/Game/Code/Screens/UScreenSingModi.pas
+++ b/Game/Code/Screens/UScreenSingModi.pas
@@ -277,8 +277,9 @@ var
Min: integer;
Sec: integer;
Tekst: string;
- S, I: integer;
+ S, I: integer;
T: integer;
+ CurLyricsTime: real;
begin
Result := false;
@@ -511,8 +512,10 @@ begin
if DLLMan.Selected.LoadSong then
begin
// update static menu with time ...
- Min := Round(LineState.CurrentTime) div 60;
- Sec := Round(LineState.CurrentTime) mod 60;
+ CurLyricsTime := LineState.GetCurrentTime();
+ Min := Round(CurLyricsTime) div 60;
+ Sec := Round(CurLyricsTime) mod 60;
+
Text[TextTimeText].Text := '';
if Min < 10 then Text[TextTimeText].Text := '0';
Text[TextTimeText].Text := Text[TextTimeText].Text + IntToStr(Min) + ':';
@@ -540,7 +543,7 @@ begin
if ShowFinish then begin
if DllMan.Selected.LoadSong then
begin
- if (not AudioPlayback.Finished) and ((CurrentSong.Finish = 0) or (LineState.CurrentTime*1000 <= CurrentSong.Finish)) then begin
+ if (not AudioPlayback.Finished) and ((CurrentSong.Finish = 0) or (LineState.GetCurrentTime*1000 <= CurrentSong.Finish)) then begin
//Pause Mod:
if not Paused then
Sing(Self); // analyze song