diff options
Diffstat (limited to '')
-rw-r--r-- | src/base/ULyrics.pas | 726 |
1 files changed, 0 insertions, 726 deletions
diff --git a/src/base/ULyrics.pas b/src/base/ULyrics.pas deleted file mode 100644 index 3f62db9c..00000000 --- a/src/base/ULyrics.pas +++ /dev/null @@ -1,726 +0,0 @@ -{* UltraStar Deluxe - Karaoke Game - * - * UltraStar Deluxe is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - * $URL$ - * $Id$ - *} - -unit ULyrics; - -interface - -{$IFDEF FPC} - {$MODE Delphi} -{$ENDIF} - -{$I switches.inc} - -uses - gl, - glext, - UTexture, - UThemes, - UMusic; - -type - // stores two textures for enabled/disabled states - TPlayerIconTex = array [0..1] of TTexture; - - TLyricsEffect = (lfxSimple, lfxZoom, lfxSlide, lfxBall, lfxShift); - - PLyricWord = ^TLyricWord; - TLyricWord = record - X: real; // left corner - Width: real; // width - Start: cardinal; // start of the word in quarters (beats) - Length: cardinal; // length of the word in quarters - Text: UTF8String; // text - Freestyle: boolean; // is freestyle? - end; - TLyricWordArray = array of TLyricWord; - - TLyricLine = class - public - Text: UTF8String; // text - Width: real; // width - Height: real; // height - Words: TLyricWordArray; // words in this line - CurWord: integer; // current active word idx (only valid if line is active) - Start: integer; // start of this line in quarters (Note: negative start values are possible due to gap) - StartNote: integer; // start of the first note of this line in quarters - Length: integer; // length in quarters (from start of first to the end of the last note) - Players: byte; // players that should sing that line (bitset, Player1: 1, Player2: 2, Player3: 4) - LastLine: boolean; // is this the last line of the song? - - constructor Create(); - destructor Destroy(); override; - procedure Reset(); - end; - - TLyricEngine = class - private - LastDrawBeat: real; - UpperLine: TLyricLine; // first line displayed (top) - LowerLine: TLyricLine; // second lind displayed (bottom) - QueueLine: TLyricLine; // third line (will be displayed when lower line is finished) - - IndicatorTex: TTexture; // texture for lyric indikator - BallTex: TTexture; // texture of the ball for the lyric effect - - QueueFull: boolean; // set to true if the queue is full and a line will be replaced with the next AddLine - LCounter: integer; // line counter - - // duet mode - textures for player icons - // FIXME: do not use a fixed player count, use MAX_PLAYERS instead - PlayerIconTex: array[0..5] of TPlayerIconTex; - - // Some helper procedures for lyric drawing - procedure DrawLyrics (Beat: real); - procedure UpdateLineMetrics(LyricLine: TLyricLine); - procedure DrawLyricsWords(LyricLine: TLyricLine; X, Y: real; StartWord, EndWord: integer); - procedure DrawLyricsLine(X, W, Y, H: real; Line: TLyricLine; Beat: real); - procedure DrawPlayerIcon(Player: byte; Enabled: boolean; X, Y: real; Size, Alpha: real); - procedure DrawBall(XBall, YBall, Alpha: real); - - public - // positions, line specific settings - UpperLineX: real; // X start-pos of UpperLine - UpperLineW: real; // Width of UpperLine with icon(s) and text - UpperLineY: real; // Y start-pos of UpperLine - UpperLineH: real; // Max. font-size of lyrics text in UpperLine - - LowerLineX: real; // X start-pos of LowerLine - LowerLineW: real; // Width of LowerLine with icon(s) and text - LowerLineY: real; // Y start-pos of LowerLine - LowerLineH: real; // Max. font-size of lyrics text in LowerLine - - // display propertys - LineColor_en: TRGBA; // Color of words in an enabled line - LineColor_dis: TRGBA; // Color of words in a disabled line - LineColor_act: TRGBA; // Color of the active word - FontStyle: byte; // Font for the lyric text - - { // currently not used - FadeInEffect: byte; // Effect for line fading in: 0: No Effect; 1: Fade Effect; 2: Move Upwards from Bottom to Pos - FadeOutEffect: byte; // Effect for line fading out: 0: No Effect; 1: Fade Effect; 2: Move Upwards - } - - // song specific settings - BPM: real; - Resolution: integer; - - // properties to easily read options of this class - property IsQueueFull: boolean read QueueFull; // line in queue? - property LineCounter: integer read LCounter; // lines that were progressed so far (after last clear) - - procedure AddLine(Line: PLine); // adds a line to the queue, if there is space - procedure Draw (Beat: real); // draw the current (active at beat) lyrics - - // clears all cached song specific information - procedure Clear(cBPM: real = 0; cResolution: integer = 0); - - function GetUpperLine(): TLyricLine; - function GetLowerLine(): TLyricLine; - - function GetUpperLineIndex(): integer; - - constructor Create(ULX, ULY, ULW, ULH, LLX, LLY, LLW, LLH: real); - procedure LoadTextures; - destructor Destroy; override; - end; - -implementation - -uses - SysUtils, - USkins, - TextGL, - UGraphic, - UDisplay, - ULog, - math, - UIni; - -{ 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; -end; - - -{ TLyricEngine } - -{** - * Initializes the engine. - *} -constructor TLyricEngine.Create(ULX, ULY, ULW, ULH, LLX, LLY, LLW, LLH: real); -begin - inherited Create(); - - BPM := 0; - Resolution := 0; - LCounter := 0; - QueueFull := False; - - UpperLine := TLyricLine.Create; - LowerLine := TLyricLine.Create; - QueueLine := TLyricLine.Create; - - LastDrawBeat := 0; - - UpperLineX := ULX; - UpperLineW := ULW; - UpperLineY := ULY; - UpperLineH := ULH; - - LowerLineX := LLX; - LowerLineW := LLW; - LowerLineY := LLY; - LowerLineH := LLH; - - LoadTextures; -end; - - -{** - * Frees memory. - *} -destructor TLyricEngine.Destroy; -begin - UpperLine.Free; - LowerLine.Free; - QueueLine.Free; - inherited; -end; - -{** - * Clears all cached Song specific Information. - *} -procedure TLyricEngine.Clear(cBPM: real; cResolution: integer); -begin - BPM := cBPM; - Resolution := cResolution; - LCounter := 0; - QueueFull := False; - - LastDrawBeat:=0; -end; - - -{** - * Loads textures needed for the drawing the lyrics, - * player icons, a ball for the ball effect and the lyric indicator. - *} -procedure TLyricEngine.LoadTextures; -var - I: Integer; -begin - // lyric indicator (bar that indicates when the line start) - IndicatorTex := Texture.LoadTexture(Skin.GetTextureFileName('LyricHelpBar'), TEXTURE_TYPE_TRANSPARENT, $FF00FF); - - // ball for current word hover in ball effect - BallTex := Texture.LoadTexture(Skin.GetTextureFileName('Ball'), TEXTURE_TYPE_TRANSPARENT, 0); - - // duet mode: load player icon - for I := 0 to 5 do - begin - PlayerIconTex[I][0] := Texture.LoadTexture(Skin.GetTextureFileName('LyricIcon_P' + InttoStr(I+1)), TEXTURE_TYPE_TRANSPARENT, 0); - PlayerIconTex[I][1] := Texture.LoadTexture(Skin.GetTextureFileName('LyricIconD_P' + InttoStr(I+1)), TEXTURE_TYPE_TRANSPARENT, 0); - end; -end; - -{** - * Adds LyricLine to queue. - * The LyricEngine stores three lines in its queue: - * UpperLine: the upper line displayed in the lyrics - * LowerLine: the lower line displayed in the lyrics - * QueueLine: an offscreen line that precedes LowerLine - * If the queue is full the next call to AddLine will replace UpperLine with - * LowerLine, LowerLine with QueueLine and QueueLine with the Line parameter. - *} -procedure TLyricEngine.AddLine(Line: PLine); -var - LyricLine: TLyricLine; - I: integer; -begin - // only add lines, if there is space - if not IsQueueFull then - begin - // set LyricLine to line to write to - if (LineCounter = 0) then - LyricLine := UpperLine - else if (LineCounter = 1) then - LyricLine := LowerLine - else - begin - // now the queue is full - LyricLine := QueueLine; - QueueFull := True; - end; - end - else - begin // rotate lines (round-robin-like) - LyricLine := UpperLine; - UpperLine := LowerLine; - LowerLine := QueueLine; - QueueLine := LyricLine; - end; - - // reset line state - LyricLine.Reset(); - - // check if sentence has notes - if (Line <> nil) and (Length(Line.Note) > 0) then - begin - // copy values from SongLine to LyricLine - LyricLine.Start := Line.Start; - LyricLine.StartNote := Line.Note[0].Start; - LyricLine.Length := Line.Note[High(Line.Note)].Start + - Line.Note[High(Line.Note)].Length - - Line.Note[0].Start; - LyricLine.LastLine := Line.LastLine; - - // copy words - SetLength(LyricLine.Words, Length(Line.Note)); - for I := 0 to High(Line.Note) do - begin - LyricLine.Words[I].Start := Line.Note[I].Start; - LyricLine.Words[I].Length := Line.Note[I].Length; - LyricLine.Words[I].Text := Line.Note[I].Text; - LyricLine.Words[I].Freestyle := Line.Note[I].NoteType = ntFreestyle; - - LyricLine.Text := LyricLine.Text + LyricLine.Words[I].Text; - end; - - UpdateLineMetrics(LyricLine); - end; - - // increase the counter - Inc(LCounter); -end; - -{** - * Draws Lyrics. - * Draw just manages the Lyrics, drawing is done by a call of DrawLyrics. - * @param Beat: current Beat in Quarters - *} -procedure TLyricEngine.Draw(Beat: real); -begin - DrawLyrics(Beat); - LastDrawBeat := Beat; -end; - -{** - * Main Drawing procedure. - *} -procedure TLyricEngine.DrawLyrics(Beat: real); -begin - DrawLyricsLine(UpperLineX, UpperLineW, UpperLineY, UpperLineH, UpperLine, Beat); - DrawLyricsLine(LowerLineX, LowerLineW, LowerLineY, LowerLineH, LowerLine, Beat); -end; - -{** - * Draws a Player's icon. - *} -procedure TLyricEngine.DrawPlayerIcon(Player: byte; Enabled: boolean; X, Y: real; Size, Alpha: real); -var - IEnabled: byte; -begin - if Enabled then - IEnabled := 0 - else - IEnabled := 1; - - glEnable(GL_TEXTURE_2D); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glBindTexture(GL_TEXTURE_2D, PlayerIconTex[Player][IEnabled].TexNum); - - glColor4f(1, 1, 1, Alpha); - glBegin(GL_QUADS); - glTexCoord2f(0, 0); glVertex2f(X, Y); - glTexCoord2f(0, 1); glVertex2f(X, Y + Size); - glTexCoord2f(1, 1); glVertex2f(X + Size, Y + Size); - glTexCoord2f(1, 0); glVertex2f(X + Size, Y); - glEnd; - - glDisable(GL_BLEND); - glDisable(GL_TEXTURE_2D); -end; - -{** - * Draws the Ball over the LyricLine if needed. - *} -procedure TLyricEngine.DrawBall(XBall, YBall, Alpha: real); -begin - glEnable(GL_TEXTURE_2D); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glBindTexture(GL_TEXTURE_2D, BallTex.TexNum); - - glColor4f(1, 1, 1, Alpha); - glBegin(GL_QUADS); - glTexCoord2f(0, 0); glVertex2f(XBall - 10, YBall); - glTexCoord2f(0, 1); glVertex2f(XBall - 10, YBall + 20); - glTexCoord2f(1, 1); glVertex2f(XBall + 10, YBall + 20); - glTexCoord2f(1, 0); glVertex2f(XBall + 10, YBall); - glEnd; - - glDisable(GL_BLEND); - glDisable(GL_TEXTURE_2D); -end; - -procedure TLyricEngine.DrawLyricsWords(LyricLine: TLyricLine; - X, Y: real; StartWord, EndWord: integer); -var - I: integer; - PosX: real; - CurWord: PLyricWord; -begin - PosX := X; - - // set word positions and line size and draw the line - for I := StartWord to EndWord do - begin - CurWord := @LyricLine.Words[I]; - SetFontItalic(CurWord.Freestyle); - SetFontPos(PosX, Y); - glPrint(CurWord.Text); - PosX := PosX + CurWord.Width; - end; -end; - -procedure TLyricEngine.UpdateLineMetrics(LyricLine: TLyricLine); -var - I: integer; - PosX: real; - CurWord: PLyricWord; - RequestWidth, RequestHeight: real; -begin - PosX := 0; - - // setup font - SetFontStyle(FontStyle); - ResetFont(); - - // check if line is lower or upper line and set sizes accordingly - // Note: at the moment upper and lower lines have same width/height - // and this function is just called by AddLine() but this may change - // so that it is called by DrawLyricsLine(). - //if (LyricLine = LowerLine) then - //begin - // RequestWidth := LowerLineW; - // RequestHeight := LowerLineH; - //end - //else - //begin - RequestWidth := UpperLineW; - RequestHeight := UpperLineH; - //end; - - // set font size to a reasonable value - LyricLine.Height := RequestHeight * 0.9; - SetFontSize(LyricLine.Height); - LyricLine.Width := glTextWidth(LyricLine.Text); - - // change font-size to fit into the lyric bar - if (LyricLine.Width > RequestWidth) then - begin - LyricLine.Height := Trunc(LyricLine.Height * (RequestWidth / LyricLine.Width)); - // the line is very loooong, set font to at least 1px - if (LyricLine.Height < 1) then - LyricLine.Height := 1; - - SetFontSize(LyricLine.Height); - LyricLine.Width := glTextWidth(LyricLine.Text); - end; - - // calc word positions and widths - for I := 0 to High(LyricLine.Words) do - begin - CurWord := @LyricLine.Words[I]; - - // - if current word is italic but not the next word get the width of the - // italic font to avoid overlapping. - // - if two italic words follow each other use the normal style's - // width otherwise the spacing between the words will be too big. - // - if it is the line's last word use normal width - if CurWord.Freestyle and - (I+1 < Length(LyricLine.Words)) and - (not LyricLine.Words[I+1].Freestyle) then - begin - SetFontItalic(true); - end; - - CurWord.X := PosX; - CurWord.Width := glTextWidth(CurWord.Text); - PosX := PosX + CurWord.Width; - SetFontItalic(false); - end; -end; - - -{** - * Draws one LyricLine - *} -procedure TLyricEngine.DrawLyricsLine(X, W, Y, H: real; Line: TLyricLine; Beat: real); -var - CurWord: PLyricWord; // current word - LastWord: PLyricWord; // last word in line - NextWord: PLyricWord; // word following current word - Progress: real; // progress of singing the current word - LyricX, LyricY: real; // left/top lyric position - WordY: real; // word y-position - LyricsEffect: TLyricsEffect; - Alpha: real; // alphalevel to fade out at end - ClipPlaneEq: array[0..3] of GLdouble; // clipping plane for slide effect - {// duet mode - IconSize: real; // size of player icons - IconAlpha: real; // alpha level of player icons - } -begin - // do not draw empty lines - if (Length(Line.Words) = 0) then - Exit; - - { - // duet mode - IconSize := (2 * Height); - 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); - } - - // set font size and style - SetFontStyle(FontStyle); - ResetFont(); - SetFontSize(Line.Height); - - // center lyrics - LyricX := X + (W - Line.Width) / 2; - LyricY := Y + (H - Line.Height) / 2; - // get lyrics effect - LyricsEffect := TLyricsEffect(Ini.LyricsEffect); - - // TODO: what about alpha in freetype outline fonts? - Alpha := 1; - - // check if this line is active (at least its first note must be active) - if (Beat >= Line.StartNote) then - begin - // if this line just got active, CurWord is -1, - // this means we should try to make the first word active - if (Line.CurWord = -1) then - Line.CurWord := 0; - - // check if the current active word is still active. - // Otherwise proceed to the next word if there is one in this line. - // Note: the max. value of Line.CurWord is High(Line.Words) - if (Line.CurWord < High(Line.Words)) and - (Beat >= Line.Words[Line.CurWord + 1].Start) then - begin - Inc(Line.CurWord); - end; - - // determine current and last word in this line. - // If the end of the line is reached use the last word as current word. - LastWord := @Line.Words[High(Line.Words)]; - CurWord := @Line.Words[Line.CurWord]; - if (Line.CurWord+1 < Length(Line.Words)) then - NextWord := @Line.Words[Line.CurWord+1] - else - NextWord := nil; - - // calc the progress of the lyrics effect - Progress := (Beat - CurWord.Start) / CurWord.Length; - if (Progress >= 1) then - 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; - - // draw sentence before current word - if (LyricsEffect in [lfxSimple, lfxBall, lfxShift]) then - // only highlight current word and not that ones before in this line - glColorRGB(LineColor_en, Alpha) - else - glColorRGB(LineColor_act, Alpha); - DrawLyricsWords(Line, LyricX, LyricY, 0, Line.CurWord-1); - - // draw rest of sentence (without current word) - glColorRGB(LineColor_en, Alpha); - if (NextWord <> nil) then - begin - DrawLyricsWords(Line, LyricX + NextWord.X, LyricY, - Line.CurWord+1, High(Line.Words)); - end; - - // draw current word - if (LyricsEffect in [lfxSimple, lfxBall, lfxShift]) then - begin - if (LyricsEffect = lfxShift) then - WordY := LyricY - 8 * (1-Progress) - else - WordY := LyricY; - - // change the color of the current word - glColor4f(LineColor_act.r, LineColor_act.g, LineColor_act.b, Alpha); - DrawLyricsWords(Line, LyricX + CurWord.X, WordY, Line.CurWord, Line.CurWord); - end - // change color and zoom current word - else if (LyricsEffect = lfxZoom) then - begin - glPushMatrix; - - // zoom at word center - glTranslatef(LyricX + CurWord.X + CurWord.Width/2, - LyricY + Line.Height/2, 0); - glScalef(1.0 + (1-Progress) * 0.5, 1.0 + (1-Progress) * 0.5, 1.0); - - glColor4f(LineColor_act.r, LineColor_act.g, LineColor_act.b, Alpha); - DrawLyricsWords(Line, -CurWord.Width/2, -Line.Height/2, Line.CurWord, Line.CurWord); - - glPopMatrix; - end - // split current word into active and non-active part - else if (LyricsEffect = lfxSlide) then - begin - // enable clipping and set clip equation coefficients to zeros - glEnable(GL_CLIP_PLANE0); - FillChar(ClipPlaneEq[0], SizeOf(ClipPlaneEq), 0); - - glPushMatrix; - glTranslatef(LyricX + CurWord.X, LyricY, 0); - - // clip non-active right part of the current word - ClipPlaneEq[0] := -1; - ClipPlaneEq[3] := CurWord.Width * Progress; - glClipPlane(GL_CLIP_PLANE0, @ClipPlaneEq); - // and draw active left part - glColor4f(LineColor_act.r, LineColor_act.g, LineColor_act.b, Alpha); - DrawLyricsWords(Line, 0, 0, Line.CurWord, Line.CurWord); - - // clip active left part of the current word - ClipPlaneEq[0] := -ClipPlaneEq[0]; - ClipPlaneEq[3] := -ClipPlaneEq[3]; - glClipPlane(GL_CLIP_PLANE0, @ClipPlaneEq); - // and draw non-active right part - glColor4f(LineColor_en.r, LineColor_en.g, LineColor_en.b, Alpha); - DrawLyricsWords(Line, 0, 0, Line.CurWord, Line.CurWord); - - glPopMatrix; - - glDisable(GL_CLIP_PLANE0); - end; - - // draw the ball onto the current word - if (LyricsEffect = lfxBall) then - begin - DrawBall(LyricX + CurWord.X + CurWord.Width * Progress, - LyricY - 15 - 15*sin(Progress * Pi), Alpha); - end; - end - else - begin - // this section is called if the whole line can be drawn at once and no - // word is highlighted. - - // enable the upper, disable the lower line - if (Line = UpperLine) then - glColorRGB(LineColor_en) - else - glColorRGB(LineColor_dis); - - DrawLyricsWords(Line, LyricX, LyricY, 0, High(Line.Words)); - end; -end; - -{** - * @returns a reference to the upper line - *} -function TLyricEngine.GetUpperLine(): TLyricLine; -begin - Result := UpperLine; -end; - -{** - * @returns a reference to the lower line - *} -function TLyricEngine.GetLowerLine(): TLyricLine; -begin - Result := LowerLine; -end; - -{** - * @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. - |