From 517e6872ed12e6a9144c4fbd0b02063feeab5528 Mon Sep 17 00:00:00 2001 From: tobigun Date: Sat, 19 Jul 2008 12:48:23 +0000 Subject: lyric engine patches, solves - pixeled/transparent fonts bug - some lines were erroneously marked active or disabled - state was not initialized correctly - negative gap problem (TLyricLine.Start was of type cardinal) - some more lyrics bugs git-svn-id: svn://svn.code.sf.net/p/ultrastardx/svn/trunk@1211 b956fd51-792f-4845-bead-9b4dfca2ff2c --- Game/Code/Classes/UDraw.pas | 46 +-- Game/Code/Classes/ULyrics.pas | 700 ++++++++++++++++++++-------------- Game/Code/Classes/UMain.pas | 77 +--- Game/Code/Classes/UMusic.pas | 81 +++- Game/Code/Screens/UScreenSing.pas | 112 +++--- Game/Code/Screens/UScreenSingModi.pas | 11 +- 6 files changed, 568 insertions(+), 459 deletions(-) (limited to 'Game') 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 -- cgit v1.2.3