From 2bd6d1d2ea8d36eedac96053a7e4a818da22f654 Mon Sep 17 00:00:00 2001 From: s_alexander Date: Thu, 3 Apr 2008 15:00:10 +0000 Subject: rewriting of the txt file parser removed unused variables from TLines and TLine, removed TMelody overworked the TLines and TLine records git-svn-id: svn://svn.code.sf.net/p/ultrastardx/svn/trunk@1000 b956fd51-792f-4845-bead-9b4dfca2ff2c --- Game/Code/Classes/UDraw.pas | 48 +- Game/Code/Classes/UFiles.pas | 6 +- Game/Code/Classes/ULyrics.pas | 47 +- Game/Code/Classes/ULyrics_bak.pas | 2 +- Game/Code/Classes/UMain.pas | 2253 +++++++++++++++++----------------- Game/Code/Classes/UMusic.pas | 21 +- Game/Code/Classes/USong.pas | 365 +++--- Game/Code/Classes/USong_TextFile.pas | 86 ++ Game/Code/Classes/USong_Txt.pas | 438 +++++++ 9 files changed, 1903 insertions(+), 1363 deletions(-) create mode 100644 Game/Code/Classes/USong_TextFile.pas create mode 100644 Game/Code/Classes/USong_Txt.pas (limited to 'Game/Code/Classes') diff --git a/Game/Code/Classes/UDraw.pas b/Game/Code/Classes/UDraw.pas index 18c347a7..4b7d02fc 100644 --- a/Game/Code/Classes/UDraw.pas +++ b/Game/Code/Classes/UDraw.pas @@ -202,16 +202,16 @@ var Pet: integer; TempR: real; begin - TempR := (Right-Left) / (Lines[NrCzesci].Line[Lines[NrCzesci].Current].End_ - Lines[NrCzesci].Line[Lines[NrCzesci].Current].StartNote); + TempR := (Right-Left) / (Lines[NrCzesci].Line[Lines[NrCzesci].Current].End_ - Lines[NrCzesci].Line[Lines[NrCzesci].Current].Note[0].Start); glEnable(GL_BLEND); glBegin(GL_LINES); - for Pet := Lines[NrCzesci].Line[Lines[NrCzesci].Current].StartNote to Lines[NrCzesci].Line[Lines[NrCzesci].Current].End_ do begin + for Pet := Lines[NrCzesci].Line[Lines[NrCzesci].Current].Note[0].Start to Lines[NrCzesci].Line[Lines[NrCzesci].Current].End_ do begin if (Pet mod Lines[NrCzesci].Resolution) = Lines[NrCzesci].NotesGAP then glColor4f(0, 0, 0, 1) else glColor4f(0, 0, 0, 0.3); - glVertex2f(Left + TempR * (Pet - Lines[NrCzesci].Line[Lines[NrCzesci].Current].StartNote), Top); - glVertex2f(Left + TempR * (Pet - Lines[NrCzesci].Line[Lines[NrCzesci].Current].StartNote), Top + 135); + glVertex2f(Left + TempR * (Pet - Lines[NrCzesci].Line[Lines[NrCzesci].Current].Note[0].Start), Top); + glVertex2f(Left + TempR * (Pet - Lines[NrCzesci].Line[Lines[NrCzesci].Current].Note[0].Start), Top + 135); end; glEnd; glDisable(GL_BLEND); @@ -248,7 +248,7 @@ begin glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); lTmpA := (Right-Left); - lTmpB := (Lines[NrCzesci].Line[Lines[NrCzesci].Current].End_ - Lines[NrCzesci].Line[Lines[NrCzesci].Current].StartNote); + lTmpB := (Lines[NrCzesci].Line[Lines[NrCzesci].Current].End_ - Lines[NrCzesci].Line[Lines[NrCzesci].Current].Note[0].Start); if ( lTmpA > 0 ) AND ( lTmpB > 0 ) THEN @@ -264,22 +264,22 @@ begin with Lines[NrCzesci].Line[Lines[NrCzesci].Current] do begin for Pet := 0 to HighNote do begin with Note[Pet] do begin - if not FreeStyle then begin + if NoteType <> ntFreestyle then begin if Ini.EffectSing = 0 then // If Golden note Effect of then Change not Color begin case NoteType of - 1: glColor4f(1, 1, 1, 1); // We set alpha to 1, cause we can control the transparency through the png itself - 2: glColor4f(1, 1, 0.3, 1); // no stars, paint yellow -> glColor4f(1, 1, 0.3, 0.85); - we could + ntNormal: glColor4f(1, 1, 1, 1); // We set alpha to 1, cause we can control the transparency through the png itself + ntGolden: glColor4f(1, 1, 0.3, 1); // no stars, paint yellow -> glColor4f(1, 1, 0.3, 0.85); - we could end; // case end //Else all Notes same Color else glColor4f(1, 1, 1, 1); // We set alpha to 1, cause we can control the transparency through the png itself // Czesci == teil, element == piece, element | koniec == end / ending // lewa czesc - left part - Rec.Left := (Start-Lines[NrCzesci].Line[Lines[NrCzesci].Current].StartNote) * TempR + Left + 0.5 + 10*ScreenX; + Rec.Left := (Start-Lines[NrCzesci].Line[Lines[NrCzesci].Current].Note[0].Start) * TempR + Left + 0.5 + 10*ScreenX; Rec.Right := Rec.Left + NotesW; Rec.Top := Top - (Tone-BaseNote)*Space/2 - NotesH; Rec.Bottom := Rec.Top + 2 * NotesH; @@ -297,7 +297,7 @@ begin // srodkowa czesc - middle part Rec.Left := Rec.Right; - Rec.Right := (Start+Length-Lines[NrCzesci].Line[Lines[NrCzesci].Current].StartNote) * TempR + Left - NotesW - 0.5 + 10*ScreenX; // Dlugosc == length + Rec.Right := (Start+Length-Lines[NrCzesci].Line[Lines[NrCzesci].Current].Note[0].Start) * TempR + Left - NotesW - 0.5 + 10*ScreenX; // Dlugosc == length glBindTexture(GL_TEXTURE_2D, Tex_plain_Mid[PlayerNumber].TexNum); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ); @@ -322,7 +322,7 @@ begin glEnd; // Golden Star Patch - if (NoteType = 2) AND (Ini.EffectSing=1) then + if (NoteType = ntGolden) AND (Ini.EffectSing=1) then begin GoldenRec.SaveGoldenStarsRec(GoldenStarPos, Rec.Top, Rec.Right, Rec.Bottom); end; @@ -365,13 +365,13 @@ var //// if Player[NrGracza].IlNut > 0 then begin - TempR := W / (Lines[0].Line[Lines[0].Current].End_ - Lines[0].Line[Lines[0].Current].StartNote); + TempR := W / (Lines[0].Line[Lines[0].Current].End_ - Lines[0].Line[Lines[0].Current].Note[0].Start); for N := 0 to Player[NrGracza].HighNote do begin with Player[NrGracza].Note[N] do begin // Left part of note - Rec.Left := X + (Start-Lines[0].Line[Lines[0].Current].StartNote) * TempR + 0.5 + 10*ScreenX; + Rec.Left := X + (Start-Lines[0].Line[Lines[0].Current].Note[0].Start) * TempR + 0.5 + 10*ScreenX; Rec.Right := Rec.Left + NotesW; // Draw it in half size, if not hit @@ -399,7 +399,7 @@ var // Middle part of the note Rec.Left := Rec.Right; - Rec.Right := X + (Start+Length-Lines[0].Line[Lines[0].Current].StartNote) * TempR - NotesW - 0.5 + 10*ScreenX; + Rec.Right := X + (Start+Length-Lines[0].Line[Lines[0].Current].Note[0].Start) * TempR - NotesW - 0.5 + 10*ScreenX; // (nowe) - dunno if (Start+Length-1 = LineState.CurrentBeatD) then @@ -480,7 +480,7 @@ begin lTmpA := (Right-Left); - lTmpB := (Lines[NrCzesci].Line[Lines[NrCzesci].Current].End_ - Lines[NrCzesci].Line[Lines[NrCzesci].Current].StartNote); + lTmpB := (Lines[NrCzesci].Line[Lines[NrCzesci].Current].End_ - Lines[NrCzesci].Line[Lines[NrCzesci].Current].Note[0].Start); if ( lTmpA > 0 ) AND @@ -496,16 +496,16 @@ begin with Lines[NrCzesci].Line[Lines[NrCzesci].Current] do begin for Pet := 0 to HighNote do begin with Note[Pet] do begin - if not FreeStyle then begin + if NoteType <> ntFreestyle then begin // begin: 14, 20 // easy: 6, 11 W := NotesW * 2 + 2; H := NotesH * 1.5 + 3.5; - X2 := (Start-Lines[NrCzesci].Line[Lines[NrCzesci].Current].StartNote) * TempR + Left + 0.5 + 10*ScreenX + 4; // wciecie + X2 := (Start-Lines[NrCzesci].Line[Lines[NrCzesci].Current].Note[0].Start) * TempR + Left + 0.5 + 10*ScreenX + 4; // wciecie X1 := X2-W; - X3 := (Start+Length-Lines[NrCzesci].Line[Lines[NrCzesci].Current].StartNote) * TempR + Left - 0.5 + 10*ScreenX - 4; // wciecie + X3 := (Start+Length-Lines[NrCzesci].Line[Lines[NrCzesci].Current].Note[0].Start) * TempR + Left - 0.5 + 10*ScreenX - 4; // wciecie X4 := X3+W; // left @@ -1235,22 +1235,22 @@ begin glEnable(GL_TEXTURE_2D); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - TempR := (Right-Left) / (Lines[NrCzesci].Line[Lines[NrCzesci].Current].End_ - Lines[NrCzesci].Line[Lines[NrCzesci].Current].StartNote); + TempR := (Right-Left) / (Lines[NrCzesci].Line[Lines[NrCzesci].Current].End_ - Lines[NrCzesci].Line[Lines[NrCzesci].Current].Note[0].Start); with Lines[NrCzesci].Line[Lines[NrCzesci].Current] do begin for Pet := 0 to HighNote do begin with Note[Pet] do begin // Golden Note Patch case NoteType of - 0: glColor4f(1, 1, 1, 0.35); - 1: glColor4f(1, 1, 1, 0.85); - 2: glColor4f(1, 1, 0.3, 0.85); + ntFreestyle: glColor4f(1, 1, 1, 0.35); + ntNormal: glColor4f(1, 1, 1, 0.85); + ntGolden: Glcolor4f(1, 1, 0.3, 0.85); end; // case // lewa czesc - left part - Rec.Left := (Start-Lines[NrCzesci].Line[Lines[NrCzesci].Current].StartNote) * TempR + Left + 0.5 + 10*ScreenX; + Rec.Left := (Start-Lines[NrCzesci].Line[Lines[NrCzesci].Current].Note[0].Start) * TempR + Left + 0.5 + 10*ScreenX; Rec.Right := Rec.Left + NotesW; Rec.Top := Top - (Tone-BaseNote)*Space/2 - NotesH; Rec.Bottom := Rec.Top + 2 * NotesH; @@ -1264,7 +1264,7 @@ begin // srodkowa czesc - middle part Rec.Left := Rec.Right; - Rec.Right := (Start+Length-Lines[NrCzesci].Line[Lines[NrCzesci].Current].StartNote) * TempR + Left - NotesW - 0.5 + 10*ScreenX; + Rec.Right := (Start+Length-Lines[NrCzesci].Line[Lines[NrCzesci].Current].Note[0].Start) * TempR + Left - NotesW - 0.5 + 10*ScreenX; glBindTexture(GL_TEXTURE_2D, Tex_Mid[Color].TexNum); glBegin(GL_QUADS); diff --git a/Game/Code/Classes/UFiles.pas b/Game/Code/Classes/UFiles.pas index 076db441..6b506574 100644 --- a/Game/Code/Classes/UFiles.pas +++ b/Game/Code/Classes/UFiles.pas @@ -116,9 +116,9 @@ begin //Golden + Freestyle Note Patch case Lines.Line[C].Note[N].NoteType of - 0: NoteState := 'F '; - 1: NoteState := ': '; - 2: NoteState := '* '; + ntFreestyle: NoteState := 'F '; + ntNormal: NoteState := ': '; + ntGolden: NoteState := '* '; end; // case S := NoteState + IntToStr(Start-RelativeSubTime) + ' ' + IntToStr(Length) + ' ' + IntToStr(Tone) + ' ' + Text; diff --git a/Game/Code/Classes/ULyrics.pas b/Game/Code/Classes/ULyrics.pas index 909ee835..09031cb5 100644 --- a/Game/Code/Classes/ULyrics.pas +++ b/Game/Code/Classes/ULyrics.pas @@ -368,9 +368,9 @@ 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].FreeStyle; + LyricLine.Words[I].Freestyle := Line.Note[I].NoteType = ntFreestyle; - LyricLine.HasFreestyle := LyricLine.HasFreestyle OR Line.Note[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 @@ -556,7 +556,7 @@ begin // this is actually a bit more than the real font size // it helps adjusting the "zoom-center" - LyricsHeight:=30 * (Line^.Size/10)+16; + LyricsHeight:=30.5 * (Line^.Size/10); { // duet mode @@ -572,8 +572,8 @@ begin LyricX2 := LyricX + Line^.Width; // maybe center smaller lines - LyricY := Y; - //LyricY := Y + ((Size / Line.Size - 1) * LyricsHeight) / 2; + //LyricY := Y; + LyricY := Y + ((Size / Line.Size - 1) * LyricsHeight) / 2; Alpha := 1; @@ -641,7 +641,7 @@ begin end; end; end; - + glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_TEXTURE_2D); @@ -661,6 +661,15 @@ begin glTexCoord2f((CurWordStart+FreestyleDiff)/1024, 1); glVertex2f(LyricX+CurWordStart+FreestyleDiff, LyricY); glEnd; + // draw rest of sentence + glColorRGB(LineColor_en); + glBegin(GL_QUADS); + glTexCoord2f((CurWordEnd+FreestyleDiff)/1024, 1); glVertex2f(LyricX+CurWordEnd+FreestyleDiff, LyricY); + glTexCoord2f(CurWordEnd/1024, 1-LyricsHeight/64); glVertex2f(LyricX+CurWordEnd, LyricY + LyricsHeight); + glTexCoord2f(Line^.Width/1024, 1-LyricsHeight/64); glVertex2f(LyricX2, LyricY + LyricsHeight); + glTexCoord2f(Line^.Width/1024, 1); glVertex2f(LyricX2, LyricY); + glEnd; + // draw active word: // type 0: simple lyric effect // type 3: ball lyric effect @@ -703,31 +712,21 @@ begin 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+0.0001, 1); glVertex2f(-(CurWordEnd-CurWordStart)/2+FreestyleDiff, -LyricsHeight/2); - glTexCoord2f(CurWordStart/1024+0.0001, 1-LyricsHeight/64); glVertex2f(-(CurWordEnd-CurWordStart)/2, + LyricsHeight/2); - glTexCoord2f(CurWordEnd/1024-0.0001, 1-LyricsHeight/64); glVertex2f((CurWordEnd-CurWordStart)/2, + LyricsHeight/2); - glTexCoord2f((CurWordEnd+FreestyleDiff)/1024-0.0001, 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; glColor4f(LineColor_act.r,LineColor_act.g,LineColor_act.b,1); glBegin(GL_QUADS); - glTexCoord2f((CurWordStart+FreestyleDiff)/1024+0.0001, 1); glVertex2f(-(CurWordEnd-CurWordStart)/2+FreestyleDiff, -LyricsHeight/2); - glTexCoord2f(CurWordStart/1024+0.0001, 1-LyricsHeight/64); glVertex2f(-(CurWordEnd-CurWordStart)/2, + LyricsHeight/2); - glTexCoord2f(CurWordEnd/1024-0.0001, 1-LyricsHeight/64); glVertex2f((CurWordEnd-CurWordStart)/2, + LyricsHeight/2); - glTexCoord2f((CurWordEnd+FreestyleDiff)/1024-0.0001, 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; - // draw rest of sentence - glColorRGB(LineColor_en); - glBegin(GL_QUADS); - glTexCoord2f((CurWordEnd+FreestyleDiff)/1024, 1); glVertex2f(LyricX+CurWordEnd+FreestyleDiff, LyricY); - glTexCoord2f(CurWordEnd/1024, 1-LyricsHeight/64); glVertex2f(LyricX+CurWordEnd, LyricY + LyricsHeight); - glTexCoord2f(Line^.Width/1024, 1-LyricsHeight/64); glVertex2f(LyricX2, LyricY + LyricsHeight); - glTexCoord2f(Line^.Width/1024, 1); glVertex2f(LyricX2, LyricY); - glEnd; - - glDisable(GL_TEXTURE_2D); glDisable(GL_BLEND); diff --git a/Game/Code/Classes/ULyrics_bak.pas b/Game/Code/Classes/ULyrics_bak.pas index 620a28ad..e44800d6 100644 --- a/Game/Code/Classes/ULyrics_bak.pas +++ b/Game/Code/Classes/ULyrics_bak.pas @@ -265,7 +265,7 @@ var begin Clear; for N := 0 to Lines[0].Line[NrCzesci].HighNote do begin - Italic := Lines[0].Line[NrCzesci].Note[N].FreeStyle; + Italic := Lines[0].Line[NrCzesci].Note[N].NoteType = ntFreestyle; AddWord(Lines[0].Line[NrCzesci].Note[N].Text); Text := Text + Lines[0].Line[NrCzesci].Note[N].Text; end; diff --git a/Game/Code/Classes/UMain.pas b/Game/Code/Classes/UMain.pas index 9459334d..baefeff8 100644 --- a/Game/Code/Classes/UMain.pas +++ b/Game/Code/Classes/UMain.pas @@ -1,1131 +1,1122 @@ -unit UMain; - -interface - -{$IFDEF FPC} - {$MODE Delphi} -{$ENDIF} - -{$I switches.inc} - -uses - SDL, - UGraphic, - UMusic, - URecord, - UTime, - SysUtils, - UDisplay, - UIni, - ULog, - ULyrics, - UScreenSing, - USong, - OpenGL12, - UThemes; - -type - TPlayer = record - Name: string; - - // Index in Teaminfo record - TeamID: Byte; - PlayerID: Byte; - - // Scores - Score: real; - ScoreLine: real; - ScoreGolden: real; - - ScoreI: integer; - ScoreLineI: integer; - ScoreGoldenI: integer; - ScoreTotalI: integer; - - // LineBonus Mod - ScoreLast: Real;//Last Line Score - - // PerfectLineTwinkle Mod (effect) - LastSentencePerfect: Boolean; - - //Meter: real; - - HighNote: integer; - IlNut: integer; - Note: array of record - Start: integer; - Length: integer; - Detekt: real; // accurate place, detected in the note - Tone: real; - Perfect: boolean; // true if the note matches the original one, lit the star - - // Half size Notes Patch - Hit: boolean; // true if the note Hits the Line - end; - end; - - -var - // Absolute Paths - GamePath: string; - SoundPath: string; - SongPath: string; - LogPath: string; - ThemePath: string; - SkinsPath: string; - ScreenshotsPath: string; - CoversPath: string; - LanguagesPath: string; - PluginPath: string; - VisualsPath: string; - PlayListPath: string; - - UserSongPath: string = ''; - UserCoversPath: string = ''; - UserPlaylistPath: string = ''; - - OGL: Boolean; - Done: Boolean; - Event: TSDL_event; - FileName: string; - Restart: boolean; - - // player and music info - Player: array of TPlayer; - PlayersPlay: integer; - - CurrentSong : TSong; - -procedure InitializePaths; - -Procedure Main; -procedure MainLoop; -procedure CheckEvents; -procedure Sing(Sender: TScreenSing); -procedure NewSentence(Sender: TScreenSing); -procedure NewBeat(Sender: TScreenSing); // executed when on then new beat -procedure NewBeatC(Sender: TScreenSing); // executed when on then new beat for click -procedure NewBeatD(Sender: TScreenSing); // executed when on then new beat for detection -//procedure NewHalf; // executed when in the half between beats -procedure NewNote(Sender: TScreenSing); // detect note -function GetMidBeat(Time: real): real; -function GetTimeFromBeat(Beat: integer): real; -procedure ClearScores(PlayerNum: integer); - -implementation - -uses - USongs, - UJoystick, - math, - UCommandLine, - ULanguage, - SDL_ttf, - USkins, - UCovers, - UCatCovers, - UDataBase, - UPlaylist, - UDLLManager, - UParty, - UConfig, - UCore, - UGraphicClasses, - UPluginDefs, - UPlatform; - - - - -procedure Main; -var - WndTitle: string; -begin - try - WndTitle := USDXVersionStr; - - if Platform.TerminateIfAlreadyRunning( {var} WndTitle) then - Exit; - - //------------------------------ - //StartUp - Create Classes and Load Files - //------------------------------ - USTime := TTime.Create; - - // Commandline Parameter Parser - Params := TCMDParams.Create; - - // Log + Benchmark - Log := TLog.Create; - Log.Title := WndTitle; - Log.Enabled := not Params.NoLog; - Log.BenchmarkStart(0); - - // Language - Log.BenchmarkStart(1); - Log.LogStatus('Initialize Paths', 'Initialization'); - InitializePaths; - Log.LogStatus('Load Language', 'Initialization'); - Language := TLanguage.Create; - - // Add Const Values: - Language.AddConst('US_VERSION', USDXVersionStr); - Log.BenchmarkEnd(1); - Log.LogBenchmark('Loading Language', 1); - - // SDL - Log.BenchmarkStart(1); - Log.LogStatus('Initialize SDL', 'Initialization'); - SDL_Init(SDL_INIT_VIDEO); - Log.BenchmarkEnd(1); - Log.LogBenchmark('Initializing SDL', 1); - - // SDL_ttf - Log.BenchmarkStart(1); - Log.LogStatus('Initialize SDL_ttf', 'Initialization'); - TTF_Init(); - Log.BenchmarkEnd(1); - Log.LogBenchmark('Initializing SDL_ttf', 1); - - // Skin - Log.BenchmarkStart(1); - Log.LogStatus('Loading Skin List', 'Initialization'); - Skin := TSkin.Create; - Log.BenchmarkEnd(1); - Log.LogBenchmark('Loading Skin List', 1); - - // Ini + Paths - Log.BenchmarkStart(1); - Log.LogStatus('Load Ini', 'Initialization'); - Ini := TIni.Create; - Ini.Load; - - //Load Languagefile - if (Params.Language <> -1) then - Language.ChangeLanguage(ILanguage[Params.Language]) - else - Language.ChangeLanguage(ILanguage[Ini.Language]); - - Log.BenchmarkEnd(1); - Log.LogBenchmark('Loading Ini', 1); - - // Sound - Log.BenchmarkStart(1); - Log.LogStatus('Initialize Sound', 'Initialization'); - InitializeSound(); - Log.BenchmarkEnd(1); - Log.LogBenchmark('Initializing Sound', 1); - - // Load Sound Settings from Ini - Log.BenchmarkStart(1); - Log.LogStatus('Load Sound Settings', 'Initialization'); - Ini.LoadSoundSettings; - Log.BenchmarkEnd(1); - Log.LogBenchmark('Load Sound Settings', 1); - - // Theme - Log.BenchmarkStart(1); - Log.LogStatus('Load Themes', 'Initialization'); - Theme := TTheme.Create(ThemePath + ITheme[Ini.Theme] + '.ini', Ini.Color); - Log.BenchmarkEnd(1); - Log.LogBenchmark('Loading Themes', 1); - - // Covers Cache - Log.BenchmarkStart(1); - Log.LogStatus('Creating Covers Cache', 'Initialization'); - Covers := TCovers.Create; - Log.LogBenchmark('Loading Covers Cache Array', 1); - Log.BenchmarkStart(1); - - // Category Covers - Log.BenchmarkStart(1); - Log.LogStatus('Creating Category Covers Array', 'Initialization'); - CatCovers:= TCatCovers.Create; - Log.BenchmarkEnd(1); - Log.LogBenchmark('Loading Category Covers Array', 1); - - // Songs - //Log.BenchmarkStart(1); - Log.LogStatus('Creating Song Array', 'Initialization'); - Songs := TSongs.Create; - //Songs.LoadSongList; - - Log.LogStatus('Creating 2nd Song Array', 'Initialization'); - CatSongs := TCatSongs.Create; - - Log.BenchmarkEnd(1); - Log.LogBenchmark('Loading Songs', 1); - - // PluginManager - Log.BenchmarkStart(1); - Log.LogStatus('PluginManager', 'Initialization'); - DLLMan := TDLLMan.Create; // Load PluginList - Log.BenchmarkEnd(1); - Log.LogBenchmark('Loading PluginManager', 1); - - {// Party Mode Manager - Log.BenchmarkStart(1); - Log.LogStatus('PartySession Manager', 'Initialization'); - PartySession := TPartySession.Create; //Load PartySession - - Log.BenchmarkEnd(1); - Log.LogBenchmark('Loading PartySession Manager', 1); } - - // Graphics - Log.BenchmarkStart(1); - Log.LogStatus('Initialize 3D', 'Initialization'); - Initialize3D(WndTitle); - Log.BenchmarkEnd(1); - Log.LogBenchmark('Initializing 3D', 1); - - // Score Saving System - Log.BenchmarkStart(1); - Log.LogStatus('DataBase System', 'Initialization'); - DataBase := TDataBaseSystem.Create; - - if (Params.ScoreFile = '') then - DataBase.Init ('Ultrastar.db') - else - DataBase.Init (Params.ScoreFile); - - Log.BenchmarkEnd(1); - Log.LogBenchmark('Loading DataBase System', 1); - - // Playlist Manager - Log.BenchmarkStart(1); - Log.LogStatus('Playlist Manager', 'Initialization'); - PlaylistMan := TPlaylistManager.Create; - Log.BenchmarkEnd(1); - Log.LogBenchmark('Loading Playlist Manager', 1); - - // GoldenStarsTwinkleMod - Log.BenchmarkStart(1); - Log.LogStatus('Effect Manager', 'Initialization'); - GoldenRec := TEffectManager.Create; - Log.BenchmarkEnd(1); - Log.LogBenchmark('Loading Particel System', 1); - - // Joypad - if (Ini.Joypad = 1) OR (Params.Joypad) then - begin - Log.BenchmarkStart(1); - Log.LogStatus('Initialize Joystick', 'Initialization'); - Joy := TJoy.Create; - Log.BenchmarkEnd(1); - Log.LogBenchmark('Initializing Joystick', 1); - end; - - Log.BenchmarkEnd(0); - Log.LogBenchmark('Loading Time', 0); - - Log.LogError('Creating Core'); - Core := TCore.Create( - USDXShortVersionStr, - MakeVersion(USDX_VERSION_MAJOR, - USDX_VERSION_MINOR, - USDX_VERSION_RELEASE, - chr(0)) - ); - - Log.LogError('Running Core'); - Core.Run; - - //------------------------------ - //Start- Mainloop - //------------------------------ - Log.LogStatus('Main Loop', 'Initialization'); - MainLoop; - - finally - //------------------------------ - //Finish Application - //------------------------------ - - // TODO: - // call an uninitialize routine for every initialize step - // or at least use the corresponding Free-Methods - - UnloadOpenGL; - //TTF_quit(); - SDL_Quit(); - - (* - {$ifdef WIN32} - if assigned(LCD) and (Ini.LPT = 1) then - LCD.Clear; - if assigned(Light) and (Ini.LPT = 2) then - Light.TurnOff; - {$endif} - *) - - if assigned(Log) then - begin - Log.LogStatus('Main Loop', 'Finished'); - Log.Free; - end; - end; -end; - -procedure MainLoop; -var - Delay: integer; -begin - Delay := 0; - SDL_EnableKeyRepeat(125, 125); - - CountSkipTime(); // JB - for some reason this seems to be needed when we use the SDL Timer functions. - while not Done do - begin - // joypad - if (Ini.Joypad = 1) or (Params.Joypad) then - Joy.Update; - - // keyboard events - CheckEvents; - - // display - done := not Display.Draw; - SwapBuffers; - - // light - //Light.Refresh; - - // delay - CountMidTime; - - Delay := Floor(1000 / 100 - 1000 * TimeMid); - - if Delay >= 1 then - SDL_Delay(Delay); // dynamic, maximum is 100 fps - - CountSkipTime; - - // reinitialization of graphics - if Restart then - begin - Reinitialize3D; - Restart := false; - end; - - end; -End; - -procedure CheckEvents; -begin - if Assigned(Display.NextScreen) then - Exit; - - while SDL_PollEvent( @event ) = 1 do - begin - case Event.type_ of - SDL_QUITEV: - begin - Display.Fade := 0; - Display.NextScreenWithCheck := nil; - Display.CheckOK := True; - end; - { - SDL_MOUSEBUTTONDOWN: - with Event.button Do - begin - if State = SDL_BUTTON_LEFT Then - begin - // - end; - end; - } - SDL_VIDEORESIZE: - begin - ScreenW := Event.resize.w; - ScreenH := Event.resize.h; - - screen := SDL_SetVideoMode(ScreenW, ScreenH, (Ini.Depth+1) * 16, SDL_OPENGL or SDL_RESIZABLE); - end; - SDL_KEYDOWN: - begin - // remap the "keypad enter" key to the "standard enter" key - if (Event.key.keysym.sym = SDLK_KP_ENTER) then - Event.key.keysym.sym := SDLK_RETURN; - - if (Event.key.keysym.sym = SDLK_F11) or - ((Event.key.keysym.sym = SDLK_RETURN) and - ((Event.key.keysym.modifier and KMOD_ALT) <> 0)) then // toggle full screen - begin - Ini.FullScreen := integer( not boolean( Ini.FullScreen ) ); - - if boolean( Ini.FullScreen ) then - begin - SDL_SetVideoMode(ScreenW, ScreenH, (Ini.Depth+1) * 16, SDL_OPENGL or SDL_FULLSCREEN); - SDL_ShowCursor(0); - end - else - begin - SDL_SetVideoMode(ScreenW, ScreenH, (Ini.Depth+1) * 16, SDL_OPENGL or SDL_RESIZABLE); - SDL_ShowCursor(1); - end; - - glViewPort(0, 0, ScreenW, ScreenH); - end - // ScreenShot hack. If Print is pressed-> Make screenshot and Save to Screenshots Path - else if (Event.key.keysym.sym = SDLK_SYSREQ) or (Event.key.keysym.sym = SDLK_PRINT) then - Display.ScreenShot - // popup hack... if there is a visible popup then let it handle input instead of underlying screen - // shoud be done in a way to be sure the topmost popup has preference (maybe error, then check) - else if (ScreenPopupError <> nil) and (ScreenPopupError.Visible) then - done := not ScreenPopupError.ParseInput(Event.key.keysym.sym, WideChar(Event.key.keysym.unicode), True) - else if (ScreenPopupCheck <> nil) and (ScreenPopupCheck.Visible) then - done := not ScreenPopupCheck.ParseInput(Event.key.keysym.sym, WideChar(Event.key.keysym.unicode), True) - // end of popup hack - else - begin - // check for Screen want to Exit - done := not Display.CurrentScreen^.ParseInput(Event.key.keysym.sym, WideChar(Event.key.keysym.unicode), True); - - // If Screen wants to Exit - if done then - begin - // If Question Option is enabled then Show Exit Popup - if (Ini.AskbeforeDel = 1) then - begin - Display.CurrentScreen^.CheckFadeTo(nil,'MSG_QUIT_USDX'); - end - else // When asking for exit is disabled then simply exit - begin - Display.Fade := 0; - Display.NextScreenWithCheck := nil; - Display.CheckOK := True; - end; - end; - - end; - end; - { - SDL_JOYAXISMOTION: - begin - // not implemented - end; - } - SDL_JOYBUTTONDOWN: - begin - // not implemented - end; - end; // Case - end; // While -end; - -function GetTimeForBeats(BPM, Beats: real): real; -begin - Result := 60 / BPM * Beats; -end; - -function GetBeats(BPM, msTime: real): real; -begin - Result := BPM * msTime / 60; -end; - -procedure GetMidBeatSub(BPMNum: integer; var Time: real; var CurBeat: real); -var - NewTime: real; -begin - if High(CurrentSong.BPM) = BPMNum then - begin - // last BPM - CurBeat := CurrentSong.BPM[BPMNum].StartBeat + GetBeats(CurrentSong.BPM[BPMNum].BPM, Time); - Time := 0; - end - else - begin - // not last BPM - // count how much time is it for start of the new BPM and store it in NewTime - NewTime := GetTimeForBeats(CurrentSong.BPM[BPMNum].BPM, CurrentSong.BPM[BPMNum+1].StartBeat - CurrentSong.BPM[BPMNum].StartBeat); - - // compare it to remaining time - if (Time - NewTime) > 0 then - begin - // there is still remaining time - CurBeat := CurrentSong.BPM[BPMNum].StartBeat; - Time := Time - NewTime; - end - else - begin - // there is no remaining time - CurBeat := CurrentSong.BPM[BPMNum].StartBeat + GetBeats(CurrentSong.BPM[BPMNum].BPM, Time); - Time := 0; - end; // if - end; // if -end; - -function GetMidBeat(Time: real): real; -var - CurBeat: real; - CurBPM: integer; -// TopBeat: real; -// TempBeat: real; -// TempTime: real; -begin - Result := 0; - if Length(CurrentSong.BPM) = 1 then - Result := Time * CurrentSong.BPM[0].BPM / 60; - - (* 2 BPMs *) -{ if Length(CurrentSong.BPM) > 1 then begin - (* new system *) - CurBeat := 0; - TopBeat := GetBeats(CurrentSong.BPM[0].BPM, Time); - if TopBeat > CurrentSong.BPM[1].StartBeat then begin - // analyze second BPM - Time := Time - GetTimeForBeats(CurrentSong.BPM[0].BPM, CurrentSong.BPM[1].StartBeat - CurBeat); - CurBeat := CurrentSong.BPM[1].StartBeat; - TopBeat := GetBeats(CurrentSong.BPM[1].BPM, Time); - Result := CurBeat + TopBeat; - - end - else - begin - (* pierwszy przedzial *) - Result := TopBeat; - end; - end;} - - (* more BPMs *) - if Length(CurrentSong.BPM) > 1 then - begin - CurBeat := 0; - CurBPM := 0; - while (Time > 0) do - begin - GetMidBeatSub(CurBPM, Time, CurBeat); - Inc(CurBPM); - end; - - Result := CurBeat; - end; -end; - -function GetTimeFromBeat(Beat: integer): real; -var - CurBPM: integer; -begin - Result := 0; - if Length(CurrentSong.BPM) = 1 then - Result := CurrentSong.GAP / 1000 + Beat * 60 / CurrentSong.BPM[0].BPM; - - (* more BPMs *) - if Length(CurrentSong.BPM) > 1 then - begin - Result := CurrentSong.GAP / 1000; - CurBPM := 0; - while (CurBPM <= High(CurrentSong.BPM)) and - (Beat > CurrentSong.BPM[CurBPM].StartBeat) do - begin - if (CurBPM < High(CurrentSong.BPM)) and - (Beat >= CurrentSong.BPM[CurBPM+1].StartBeat) then - begin - // full range - Result := Result + (60 / CurrentSong.BPM[CurBPM].BPM) * - (CurrentSong.BPM[CurBPM+1].StartBeat - CurrentSong.BPM[CurBPM].StartBeat); - end; - - if (CurBPM = High(CurrentSong.BPM)) or - (Beat < CurrentSong.BPM[CurBPM+1].StartBeat) then - begin - // in the middle - Result := Result + (60 / CurrentSong.BPM[CurBPM].BPM) * - (Beat - CurrentSong.BPM[CurBPM].StartBeat); - end; - Inc(CurBPM); - end; - - { - while (Time > 0) do - begin - GetMidBeatSub(CurBPM, Time, CurBeat); - Inc(CurBPM); - end; - } - end; // if} -end; - -procedure Sing(Sender: TScreenSing); -var - Count: integer; - CountGr: integer; - CP: integer; - Done: real; - N: integer; -begin - LineState.CurrentTime := LineState.CurrentTime + TimeSkip; - - LineState.OldBeat := LineState.CurrentBeat; - LineState.MidBeat := GetMidBeat(LineState.CurrentTime - (CurrentSong.Gap{ + 90 I've forgotten for what it is}) / 1000); // new system with variable BPM in function - 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; - LineState.MidBeatD := -0.5+GetMidBeat(LineState.CurrentTime - (CurrentSong.Gap + 120 + 20) / 1000); // MidBeat with addition GAP - LineState.CurrentBeatD := Floor(LineState.MidBeatD); - LineState.FracBeatD := Frac(LineState.MidBeatD); - - // sentences routines - for CountGr := 0 to 0 do //High(Gracz) - begin; - CP := CountGr; - // ustawianie starej parts - LineState.OldLine := Lines[CP].Current; - - // wybieranie aktualnej parts - for Count := 0 to Lines[CP].High do - begin - if LineState.CurrentBeat >= Lines[CP].Line[Count].Start then - Lines[CP].Current := Count; - end; - - // czysczenie nut gracza, gdy to jest nowa plansza - // (optymizacja raz na halfbeat jest zla) - if Lines[CP].Current <> LineState.OldLine then - NewSentence(Sender); - - end; // for CountGr - - // wykonuje operacje raz na beat - if (LineState.CurrentBeat >= 0) and (LineState.OldBeat <> LineState.CurrentBeat) then - NewBeat(Sender); - - // make some operations on clicks - if {(LineState.CurrentBeatC >= 0) and }(LineState.OldBeatC <> LineState.CurrentBeatC) then - NewBeatC(Sender); - - // make some operations when detecting new voice pitch - if (LineState.CurrentBeatD >= 0) and (LineState.OldBeatD <> LineState.CurrentBeatD) then - NewBeatD(Sender); - - // wykonuje operacje w polowie beatu -// if (LineState.AktHalf >= 1) and (LineState.OldHalf <> LineState.AktHalf) then -// NewHalf; - - // plynnie przesuwa text - Done := 1; - for N := 0 to Lines[0].Line[Lines[0].Current].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 - 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; - - // wylacza ostatnia nute po przejsciu - {// 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 Sender.LyricMain.Selected := -1; - - if Done > 1 then Done := 1; - Sender.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; - end; - end;} - - -end; - -procedure NewSentence(Sender: TScreenSing); -var -G: Integer; -begin - // czyszczenie nut graczy - for G := 0 to High(Player) do - begin - Player[G].IlNut := 0; - Player[G].HighNote := -1; - SetLength(Player[G].Note, 0); - end; - - // Add Words to Lyrics - with Sender 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; - - //Sender.UpdateLCD; - - //On Sentence Change... - Sender.onSentenceChange(Lines[0].Current); -end; - -procedure NewBeat(Sender: TScreenSing); -var - Count: integer; -// TempBeat: integer; -begin - // ustawia zaznaczenie tekstu -// SingScreen.LyricMain.Selected := -1; - for Count := 0 to Lines[0].Line[Lines[0].Current].HighNote do - if (Lines[0].Line[Lines[0].Current].Note[Count].Start = LineState.CurrentBeat) then - begin - // operates on currently beated note - //Todo: Lyrics - //Sender.LyricMain.Selected := Count; - -// LCD.MoveCursor(1, ScreenSing.LyricMain.SelectedLetter); -// LCD.ShowCursor; - - //LCD.MoveCursorBR(Sender.LyricMain.SelectedLetter); - //LCD.ShowCursor; - end; -end; - -procedure NewBeatC; -var - Count: integer; -// LPT_1: integer; -// LPT_2: integer; -begin -// LPT_1 := 1; -// LPT_2 := 1; - - // beat click - if (Ini.BeatClick = 1) and ((LineState.CurrentBeatC + Lines[0].Resolution + Lines[0].NotesGAP) mod Lines[0].Resolution = 0) then - AudioPlayback.PlaySound(SoundLib.Click); - - // debug system on LPT - if ((LineState.CurrentBeatC + Lines[0].Resolution + Lines[0].NotesGAP) mod Lines[0].Resolution = 0) then - begin - //LPT_1 := 0; -// Light.LightOne(0, 150); - - (* - Light.LightOne(1, 200); // beat light - if ParamStr(1) = '-doublelights' then - Light.LightOne(0, 200); // beat light - *) - - -{ if ((LineState.CurrentBeatC + Lines[0].Resolution + Lines[0].NotesGAP) mod (Lines[0].Resolution * 2) = 0) then - Light.LightOne(0, 150) - else - Light.LightOne(1, 150)} - end; - - for Count := 0 to Lines[0].Line[Lines[0].Current].HighNote do - begin - if (Lines[0].Line[Lines[0].Current].Note[Count].Start = LineState.CurrentBeatC) then - begin - // click assist - if Ini.ClickAssist = 1 then - AudioPlayback.PlaySound(SoundLib.Click); - - //LPT_2 := 0; - (* - if ParamStr(1) <> '-doublelights' then - Light.LightOne(0, 150); //125 - *) - - // drum machine - (* - TempBeat := LineState.CurrentBeat;// + 2; - if (TempBeat mod 8 = 0) then Music.PlayDrum; - if (TempBeat mod 8 = 4) then Music.PlayClap; -// if (TempBeat mod 4 = 2) then Music.PlayHihat; - if (TempBeat mod 4 <> 0) then Music.PlayHihat; - *) - end; - end; - - {$IFDEF UseSerialPort} - // PortWriteB($378, LPT_1 + LPT_2 * 2); // 0 zapala - {$ENDIF} -end; - -procedure NewBeatD(Sender: TScreenSing); -begin - NewNote(Sender); -end; - -//procedure NewHalf; -//begin -// NewNote; -//end; - -procedure NewNote(Sender: TScreenSing); -var - CP: integer; // current player - S: integer; // sentence - SMin: integer; - SMax: integer; - SDet: integer; // temporary: sentence of detected note - Count: integer; - Mozna: boolean; - New: boolean; - Range: integer; - NoteHit:boolean; -begin - // Log.LogStatus('Beat ' + IntToStr(LineState.CurrentBeat) + ' HalfBeat ' + IntToStr(LineState.AktHalf), 'NewBeat'); - - // On linux we get an AV @ NEWNOTE, line 600 of Classes/UMain.pas - if not assigned( AudioInputProcessor.Sound ) then - exit; - - // analizuje dla obu graczy ten sam sygnal (Sound.OneSrcForBoth) - // albo juz lepiej nie - for CP := 0 to PlayersPlay-1 do - begin - // analyze buffer - AudioInputProcessor.Sound[CP].AnalyzeBuffer; - - // adds some noise - //LineState.Tone := LineState.Tone + Round(Random(3)) - 1; - - // count min and max sentence range for checking (detection is delayed to the notes we see on the screen) - SMin := Lines[0].Current-1; - if SMin < 0 then - SMin := 0; - SMax := Lines[0].Current; - - // check if we can add new note - Mozna := false; - SDet:=SMin; - for S := SMin to SMax do - begin - for Count := 0 to Lines[0].Line[S].HighNote do - begin - if ((Lines[0].Line[S].Note[Count].Start <= LineState.CurrentBeatD) - and (Lines[0].Line[S].Note[Count].Start + Lines[0].Line[S].Note[Count].Length - 1 >= LineState.CurrentBeatD)) - and (not Lines[0].Line[S].Note[Count].FreeStyle) // but don't allow when it's FreeStyle note - and (Lines[0].Line[S].Note[Count].Length > 0) then // and make sure the note lengths is at least 1 - begin - SDet := S; - Mozna := true; - Break; - end; - end; - end; - - S := SDet; - - //Czas.SzczytJest := true; - //Czas.Tone := 27; - - // gdy moze, to dodaje nute - When Mozna, it adds note (?) - if (AudioInputProcessor.Sound[CP].ToneValid) and (Mozna) then - begin - // operowanie na ostatniej nucie - for Count := 0 to Lines[0].Line[S].HighNote do - begin - if (Lines[0].Line[S].Note[Count].Start <= LineState.OldBeatD+1) and - (Lines[0].Line[S].Note[Count].Start + - Lines[0].Line[S].Note[Count].Length > LineState.OldBeatD+1) then - begin - // to robi, tylko dla pary nut (oryginalnej i gracza) - - // przesuwanie tonu w odpowiednia game - while (AudioInputProcessor.Sound[CP].Tone - Lines[0].Line[S].Note[Count].Tone > 6) do - AudioInputProcessor.Sound[CP].Tone := AudioInputProcessor.Sound[CP].Tone - 12; - - while (AudioInputProcessor.Sound[CP].Tone - Lines[0].Line[S].Note[Count].Tone < -6) do - AudioInputProcessor.Sound[CP].Tone := AudioInputProcessor.Sound[CP].Tone + 12; - - // Half size Notes Patch - NoteHit := false; - - //if Ini.Difficulty = 0 then Range := 2; - //if Ini.Difficulty = 1 then Range := 1; - //if Ini.Difficulty = 2 then Range := 0; - Range := 2 - Ini.Difficulty; - - if abs(Lines[0].Line[S].Note[Count].Tone - AudioInputProcessor.Sound[CP].Tone) <= Range then - begin - AudioInputProcessor.Sound[CP].Tone := Lines[0].Line[S].Note[Count].Tone; - - // Half size Notes Patch - NoteHit := true; - - if (Ini.LineBonus = 0) then - begin - // add points without LineBonus - case Lines[0].Line[S].Note[Count].NoteType of - 1: Player[CP].Score := Player[CP].Score + 10000 / Lines[0].NoteType * - Lines[0].Line[S].Note[Count].NoteType; - 2: Player[CP].ScoreGolden := Player[CP].ScoreGolden + 10000 / Lines[0].NoteType * - Lines[0].Line[S].Note[Count].NoteType; - end; - end - else - begin - // add points with Line Bonus - case Lines[0].Line[S].Note[Count].NoteType of - 1: Player[CP].Score := Player[CP].Score + 9000 / Lines[0].NoteType * - Lines[0].Line[S].Note[Count].NoteType; - 2: Player[CP].ScoreGolden := Player[CP].ScoreGolden + 9000 / Lines[0].NoteType * - Lines[0].Line[S].Note[Count].NoteType; - end; - end; - - Player[CP].ScoreI := Floor(Player[CP].Score / 10) * 10; - Player[CP].ScoreGoldenI := Floor(Player[CP].ScoreGolden / 10) * 10; - - Player[CP].ScoreTotalI := Player[CP].ScoreI + Player[CP].ScoreGoldenI + Player[CP].ScoreLineI; - end; - - end; // operowanie - end; // for - - // sprawdzanie czy to nowa nuta, czy przedluzenie - if S = SMax then - begin - New := true; - // if last has the same tone - if (Player[CP].IlNut > 0 ) and - (Player[CP].Note[Player[CP].HighNote].Tone = AudioInputProcessor.Sound[CP].Tone) and - (Player[CP].Note[Player[CP].HighNote].Start + Player[CP].Note[Player[CP].HighNote].Length = LineState.CurrentBeatD) then - begin - New := false; - end; - - // if is not as new note to control "beacie" (TODO: translate polish "beacie") - for Count := 0 to Lines[0].Line[S].HighNote do - begin - if (Lines[0].Line[S].Note[Count].Start = LineState.CurrentBeatD) then - New := true; - end; - - // dodawanie nowej nuty - if New then - begin - // New Note - Player[CP].IlNut := Player[CP].IlNut + 1; - Player[CP].HighNote := Player[CP].HighNote + 1; - SetLength(Player[CP].Note, Player[CP].IlNut); - Player[CP].Note[Player[CP].HighNote].Start := LineState.CurrentBeatD; - Player[CP].Note[Player[CP].HighNote].Length := 1; - Player[CP].Note[Player[CP].HighNote].Tone := AudioInputProcessor.Sound[CP].Tone; // Ton || TonDokl - Player[CP].Note[Player[CP].HighNote].Detekt := LineState.MidBeat; - - // Half Note Patch - Player[CP].Note[Player[CP].HighNote].Hit := NoteHit; - - //Log.LogStatus('New Note ' + IntToStr(Gracz.Note[Gracz.HighNote].Start), 'NewBeat'); - end - else - begin - // przedluzenie nuty - Player[CP].Note[Player[CP].HighNote].Length := Player[CP].Note[Player[CP].HighNote].Length + 1; - end; - - // check for perfect note and then lit the star (on Draw) - for Count := 0 to Lines[0].Line[S].HighNote do - begin - if (Lines[0].Line[S].Note[Count].Start = Player[CP].Note[Player[CP].HighNote].Start) and - (Lines[0].Line[S].Note[Count].Length = Player[CP].Note[Player[CP].HighNote].Length) and - (Lines[0].Line[S].Note[Count].Tone = Player[CP].Note[Player[CP].HighNote].Tone) then - begin - Player[CP].Note[Player[CP].HighNote].Perfect := true; - end; - end; - end; // if S = SMax - - end; // if moze - end; // for CP - // Log.LogStatus('EndBeat', 'NewBeat'); - - //On Sentence End -> For LineBonus + SingBar - if (sDet >= low(Lines[0].Line)) and (sDet <= high(Lines[0].Line)) then - begin - if assigned( Sender ) and - ((Lines[0].Line[SDet].Note[Lines[0].Line[SDet].HighNote].Start + Lines[0].Line[SDet].Note[Lines[0].Line[SDet].HighNote].Length - 1) = LineState.CurrentBeatD) then - begin - Sender.onSentenceEnd(sDet); - end; - end; - -end; - -procedure ClearScores(PlayerNum: integer); -begin - Player[PlayerNum].Score := 0; - Player[PlayerNum].ScoreI := 0; - Player[PlayerNum].ScoreLine := 0; - Player[PlayerNum].ScoreLineI := 0; - Player[PlayerNum].ScoreGolden := 0; - Player[PlayerNum].ScoreGoldenI := 0; - Player[PlayerNum].ScoreTotalI := 0; -end; - -//-------------------- -// Function sets all Absolute Paths e.g. Song Path and makes sure the Directorys exist -//-------------------- -procedure InitializePaths; - - // Initialize a Path Variable - // After Setting Paths, make sure that Paths exist - function initialize_path( out aPathVar : string; const aLocation : string ): boolean; - var - lWriteable: Boolean; - lAttrib : integer; - begin - lWriteable := false; - aPathVar := aLocation; - - // Make sure the directory is needex - ForceDirectories(aPathVar); - - if DirectoryExists(aPathVar) then - begin - lAttrib := fileGetAttr(aPathVar); - - lWriteable := (lAttrib and faDirectory <> 0) and - not (lAttrib and faReadOnly <> 0) - end; - - if not lWriteable then - Log.LogError('Error: Dir ('+ aLocation +') is Readonly'); - - result := lWriteable; - end; - -begin - initialize_path( LogPath , Platform.GetLogPath ); - initialize_path( SoundPath , Platform.GetGameSharedPath + 'Sounds' + PathDelim ); - initialize_path( ThemePath , Platform.GetGameSharedPath + 'Themes' + PathDelim ); - initialize_path( SkinsPath , Platform.GetGameSharedPath + 'Skins' + PathDelim ); - initialize_path( LanguagesPath , Platform.GetGameSharedPath + 'Languages' + PathDelim ); - initialize_path( PluginPath , Platform.GetGameSharedPath + 'Plugins' + PathDelim ); - initialize_path( VisualsPath , Platform.GetGameSharedPath + 'Visuals' + PathDelim ); - - initialize_path( ScreenshotsPath , Platform.GetGameUserPath + 'Screenshots' + PathDelim ); - - // Users Song Path .... - initialize_path( UserSongPath , Platform.GetGameUserPath + 'Songs' + PathDelim ); - initialize_path( UserCoversPath , Platform.GetGameUserPath + 'Covers' + PathDelim ); - initialize_path( UserPlaylistPath , Platform.GetGameUserPath + 'Playlists' + PathDelim ); - - // Shared Song Path .... - initialize_path( SongPath , Platform.GetGameSharedPath + 'Songs' + PathDelim ); - initialize_path( CoversPath , Platform.GetGameSharedPath + 'Covers' + PathDelim ); - initialize_path( PlaylistPath , Platform.GetGameSharedPath + 'Playlists' + PathDelim ); - - DecimalSeparator := '.'; -end; - -end. - +unit UMain; + +interface + +{$IFDEF FPC} + {$MODE Delphi} +{$ENDIF} + +{$I switches.inc} + +uses + SDL, + UGraphic, + UMusic, + URecord, + UTime, + SysUtils, + UDisplay, + UIni, + ULog, + ULyrics, + UScreenSing, + USong, + OpenGL12, + UThemes; + +type + TPlayer = record + Name: string; + + // Index in Teaminfo record + TeamID: Byte; + PlayerID: Byte; + + // Scores + Score: real; + ScoreLine: real; + ScoreGolden: real; + + ScoreI: integer; + ScoreLineI: integer; + ScoreGoldenI: integer; + ScoreTotalI: integer; + + // LineBonus Mod + ScoreLast: Real;//Last Line Score + + // PerfectLineTwinkle Mod (effect) + LastSentencePerfect: Boolean; + + //Meter: real; + + HighNote: integer; + IlNut: integer; + Note: array of record + Start: integer; + Length: integer; + Detekt: real; // accurate place, detected in the note + Tone: real; + Perfect: boolean; // true if the note matches the original one, lit the star + + // Half size Notes Patch + Hit: boolean; // true if the note Hits the Line + end; + end; + + +var + // Absolute Paths + GamePath: string; + SoundPath: string; + SongPath: string; + LogPath: string; + ThemePath: string; + SkinsPath: string; + ScreenshotsPath: string; + CoversPath: string; + LanguagesPath: string; + PluginPath: string; + VisualsPath: string; + PlayListPath: string; + + UserSongPath: string = ''; + UserCoversPath: string = ''; + UserPlaylistPath: string = ''; + + OGL: Boolean; + Done: Boolean; + Event: TSDL_event; + FileName: string; + Restart: boolean; + + // player and music info + Player: array of TPlayer; + PlayersPlay: integer; + + CurrentSong : TSong; + +procedure InitializePaths; + +Procedure Main; +procedure MainLoop; +procedure CheckEvents; +procedure Sing(Sender: TScreenSing); +procedure NewSentence(Sender: TScreenSing); +procedure NewBeat(Sender: TScreenSing); // executed when on then new beat +procedure NewBeatC(Sender: TScreenSing); // executed when on then new beat for click +procedure NewBeatD(Sender: TScreenSing); // executed when on then new beat for detection +//procedure NewHalf; // executed when in the half between beats +procedure NewNote(Sender: TScreenSing); // detect note +function GetMidBeat(Time: real): real; +function GetTimeFromBeat(Beat: integer): real; +procedure ClearScores(PlayerNum: integer); + +implementation + +uses + USongs, + UJoystick, + math, + UCommandLine, + ULanguage, + SDL_ttf, + USkins, + UCovers, + UCatCovers, + UDataBase, + UPlaylist, + UDLLManager, + UParty, + UConfig, + UCore, + UGraphicClasses, + UPluginDefs, + UPlatform; + + + + +procedure Main; +var + WndTitle: string; +begin + try + WndTitle := USDXVersionStr; + + if Platform.TerminateIfAlreadyRunning( {var} WndTitle) then + Exit; + + //------------------------------ + //StartUp - Create Classes and Load Files + //------------------------------ + USTime := TTime.Create; + + // Commandline Parameter Parser + Params := TCMDParams.Create; + + // Log + Benchmark + Log := TLog.Create; + Log.Title := WndTitle; + Log.Enabled := not Params.NoLog; + Log.BenchmarkStart(0); + + // Language + Log.BenchmarkStart(1); + Log.LogStatus('Initialize Paths', 'Initialization'); + InitializePaths; + Log.LogStatus('Load Language', 'Initialization'); + Language := TLanguage.Create; + + // Add Const Values: + Language.AddConst('US_VERSION', USDXVersionStr); + Log.BenchmarkEnd(1); + Log.LogBenchmark('Loading Language', 1); + + // SDL + Log.BenchmarkStart(1); + Log.LogStatus('Initialize SDL', 'Initialization'); + SDL_Init(SDL_INIT_VIDEO); + Log.BenchmarkEnd(1); + Log.LogBenchmark('Initializing SDL', 1); + + // SDL_ttf + Log.BenchmarkStart(1); + Log.LogStatus('Initialize SDL_ttf', 'Initialization'); + TTF_Init(); + Log.BenchmarkEnd(1); + Log.LogBenchmark('Initializing SDL_ttf', 1); + + // Skin + Log.BenchmarkStart(1); + Log.LogStatus('Loading Skin List', 'Initialization'); + Skin := TSkin.Create; + Log.BenchmarkEnd(1); + Log.LogBenchmark('Loading Skin List', 1); + + // Ini + Paths + Log.BenchmarkStart(1); + Log.LogStatus('Load Ini', 'Initialization'); + Ini := TIni.Create; + Ini.Load; + + //Load Languagefile + if (Params.Language <> -1) then + Language.ChangeLanguage(ILanguage[Params.Language]) + else + Language.ChangeLanguage(ILanguage[Ini.Language]); + + Log.BenchmarkEnd(1); + Log.LogBenchmark('Loading Ini', 1); + + // Sound + Log.BenchmarkStart(1); + Log.LogStatus('Initialize Sound', 'Initialization'); + InitializeSound(); + Log.BenchmarkEnd(1); + Log.LogBenchmark('Initializing Sound', 1); + + // Load Sound Settings from Ini + Log.BenchmarkStart(1); + Log.LogStatus('Load Sound Settings', 'Initialization'); + Ini.LoadSoundSettings; + Log.BenchmarkEnd(1); + Log.LogBenchmark('Load Sound Settings', 1); + + // Theme + Log.BenchmarkStart(1); + Log.LogStatus('Load Themes', 'Initialization'); + Theme := TTheme.Create(ThemePath + ITheme[Ini.Theme] + '.ini', Ini.Color); + Log.BenchmarkEnd(1); + Log.LogBenchmark('Loading Themes', 1); + + // Covers Cache + Log.BenchmarkStart(1); + Log.LogStatus('Creating Covers Cache', 'Initialization'); + Covers := TCovers.Create; + Log.LogBenchmark('Loading Covers Cache Array', 1); + Log.BenchmarkStart(1); + + // Category Covers + Log.BenchmarkStart(1); + Log.LogStatus('Creating Category Covers Array', 'Initialization'); + CatCovers:= TCatCovers.Create; + Log.BenchmarkEnd(1); + Log.LogBenchmark('Loading Category Covers Array', 1); + + // Songs + //Log.BenchmarkStart(1); + Log.LogStatus('Creating Song Array', 'Initialization'); + Songs := TSongs.Create; + //Songs.LoadSongList; + + Log.LogStatus('Creating 2nd Song Array', 'Initialization'); + CatSongs := TCatSongs.Create; + + Log.BenchmarkEnd(1); + Log.LogBenchmark('Loading Songs', 1); + + // PluginManager + Log.BenchmarkStart(1); + Log.LogStatus('PluginManager', 'Initialization'); + DLLMan := TDLLMan.Create; // Load PluginList + Log.BenchmarkEnd(1); + Log.LogBenchmark('Loading PluginManager', 1); + + {// Party Mode Manager + Log.BenchmarkStart(1); + Log.LogStatus('PartySession Manager', 'Initialization'); + PartySession := TPartySession.Create; //Load PartySession + + Log.BenchmarkEnd(1); + Log.LogBenchmark('Loading PartySession Manager', 1); } + + // Graphics + Log.BenchmarkStart(1); + Log.LogStatus('Initialize 3D', 'Initialization'); + Initialize3D(WndTitle); + Log.BenchmarkEnd(1); + Log.LogBenchmark('Initializing 3D', 1); + + // Score Saving System + Log.BenchmarkStart(1); + Log.LogStatus('DataBase System', 'Initialization'); + DataBase := TDataBaseSystem.Create; + + if (Params.ScoreFile = '') then + DataBase.Init ('Ultrastar.db') + else + DataBase.Init (Params.ScoreFile); + + Log.BenchmarkEnd(1); + Log.LogBenchmark('Loading DataBase System', 1); + + // Playlist Manager + Log.BenchmarkStart(1); + Log.LogStatus('Playlist Manager', 'Initialization'); + PlaylistMan := TPlaylistManager.Create; + Log.BenchmarkEnd(1); + Log.LogBenchmark('Loading Playlist Manager', 1); + + // GoldenStarsTwinkleMod + Log.BenchmarkStart(1); + Log.LogStatus('Effect Manager', 'Initialization'); + GoldenRec := TEffectManager.Create; + Log.BenchmarkEnd(1); + Log.LogBenchmark('Loading Particel System', 1); + + // Joypad + if (Ini.Joypad = 1) OR (Params.Joypad) then + begin + Log.BenchmarkStart(1); + Log.LogStatus('Initialize Joystick', 'Initialization'); + Joy := TJoy.Create; + Log.BenchmarkEnd(1); + Log.LogBenchmark('Initializing Joystick', 1); + end; + + Log.BenchmarkEnd(0); + Log.LogBenchmark('Loading Time', 0); + + Log.LogError('Creating Core'); + Core := TCore.Create( + USDXShortVersionStr, + MakeVersion(USDX_VERSION_MAJOR, + USDX_VERSION_MINOR, + USDX_VERSION_RELEASE, + chr(0)) + ); + + Log.LogError('Running Core'); + Core.Run; + + //------------------------------ + //Start- Mainloop + //------------------------------ + Log.LogStatus('Main Loop', 'Initialization'); + MainLoop; + + finally + //------------------------------ + //Finish Application + //------------------------------ + + // TODO: + // call an uninitialize routine for every initialize step + // or at least use the corresponding Free-Methods + + UnloadOpenGL; + //TTF_quit(); + SDL_Quit(); + + (* + {$ifdef WIN32} + if assigned(LCD) and (Ini.LPT = 1) then + LCD.Clear; + if assigned(Light) and (Ini.LPT = 2) then + Light.TurnOff; + {$endif} + *) + + if assigned(Log) then + begin + Log.LogStatus('Main Loop', 'Finished'); + Log.Free; + end; + end; +end; + +procedure MainLoop; +var + Delay: integer; +begin + Delay := 0; + SDL_EnableKeyRepeat(125, 125); + + CountSkipTime(); // JB - for some reason this seems to be needed when we use the SDL Timer functions. + while not Done do + begin + // joypad + if (Ini.Joypad = 1) or (Params.Joypad) then + Joy.Update; + + // keyboard events + CheckEvents; + + // display + done := not Display.Draw; + SwapBuffers; + + // light + //Light.Refresh; + + // delay + CountMidTime; + + Delay := Floor(1000 / 100 - 1000 * TimeMid); + + if Delay >= 1 then + SDL_Delay(Delay); // dynamic, maximum is 100 fps + + CountSkipTime; + + // reinitialization of graphics + if Restart then + begin + Reinitialize3D; + Restart := false; + end; + + end; +End; + +procedure CheckEvents; +begin + if Assigned(Display.NextScreen) then + Exit; + + while SDL_PollEvent( @event ) = 1 do + begin + case Event.type_ of + SDL_QUITEV: + begin + Display.Fade := 0; + Display.NextScreenWithCheck := nil; + Display.CheckOK := True; + end; + { + SDL_MOUSEBUTTONDOWN: + with Event.button Do + begin + if State = SDL_BUTTON_LEFT Then + begin + // + end; + end; + } + SDL_VIDEORESIZE: + begin + ScreenW := Event.resize.w; + ScreenH := Event.resize.h; + + screen := SDL_SetVideoMode(ScreenW, ScreenH, (Ini.Depth+1) * 16, SDL_OPENGL or SDL_RESIZABLE); + end; + SDL_KEYDOWN: + begin + // remap the "keypad enter" key to the "standard enter" key + if (Event.key.keysym.sym = SDLK_KP_ENTER) then + Event.key.keysym.sym := SDLK_RETURN; + + if (Event.key.keysym.sym = SDLK_F11) or + ((Event.key.keysym.sym = SDLK_RETURN) and + ((Event.key.keysym.modifier and KMOD_ALT) <> 0)) then // toggle full screen + begin + Ini.FullScreen := integer( not boolean( Ini.FullScreen ) ); + + if boolean( Ini.FullScreen ) then + begin + SDL_SetVideoMode(ScreenW, ScreenH, (Ini.Depth+1) * 16, SDL_OPENGL or SDL_FULLSCREEN); + SDL_ShowCursor(0); + end + else + begin + SDL_SetVideoMode(ScreenW, ScreenH, (Ini.Depth+1) * 16, SDL_OPENGL or SDL_RESIZABLE); + SDL_ShowCursor(1); + end; + + glViewPort(0, 0, ScreenW, ScreenH); + end + // ScreenShot hack. If Print is pressed-> Make screenshot and Save to Screenshots Path + else if (Event.key.keysym.sym = SDLK_SYSREQ) or (Event.key.keysym.sym = SDLK_PRINT) then + Display.ScreenShot + // popup hack... if there is a visible popup then let it handle input instead of underlying screen + // shoud be done in a way to be sure the topmost popup has preference (maybe error, then check) + else if (ScreenPopupError <> nil) and (ScreenPopupError.Visible) then + done := not ScreenPopupError.ParseInput(Event.key.keysym.sym, WideChar(Event.key.keysym.unicode), True) + else if (ScreenPopupCheck <> nil) and (ScreenPopupCheck.Visible) then + done := not ScreenPopupCheck.ParseInput(Event.key.keysym.sym, WideChar(Event.key.keysym.unicode), True) + // end of popup hack + else + begin + // check for Screen want to Exit + done := not Display.CurrentScreen^.ParseInput(Event.key.keysym.sym, WideChar(Event.key.keysym.unicode), True); + + // If Screen wants to Exit + if done then + begin + // If Question Option is enabled then Show Exit Popup + if (Ini.AskbeforeDel = 1) then + begin + Display.CurrentScreen^.CheckFadeTo(nil,'MSG_QUIT_USDX'); + end + else // When asking for exit is disabled then simply exit + begin + Display.Fade := 0; + Display.NextScreenWithCheck := nil; + Display.CheckOK := True; + end; + end; + + end; + end; + { + SDL_JOYAXISMOTION: + begin + // not implemented + end; + } + SDL_JOYBUTTONDOWN: + begin + // not implemented + end; + end; // Case + end; // While +end; + +function GetTimeForBeats(BPM, Beats: real): real; +begin + Result := 60 / BPM * Beats; +end; + +function GetBeats(BPM, msTime: real): real; +begin + Result := BPM * msTime / 60; +end; + +procedure GetMidBeatSub(BPMNum: integer; var Time: real; var CurBeat: real); +var + NewTime: real; +begin + if High(CurrentSong.BPM) = BPMNum then + begin + // last BPM + CurBeat := CurrentSong.BPM[BPMNum].StartBeat + GetBeats(CurrentSong.BPM[BPMNum].BPM, Time); + Time := 0; + end + else + begin + // not last BPM + // count how much time is it for start of the new BPM and store it in NewTime + NewTime := GetTimeForBeats(CurrentSong.BPM[BPMNum].BPM, CurrentSong.BPM[BPMNum+1].StartBeat - CurrentSong.BPM[BPMNum].StartBeat); + + // compare it to remaining time + if (Time - NewTime) > 0 then + begin + // there is still remaining time + CurBeat := CurrentSong.BPM[BPMNum].StartBeat; + Time := Time - NewTime; + end + else + begin + // there is no remaining time + CurBeat := CurrentSong.BPM[BPMNum].StartBeat + GetBeats(CurrentSong.BPM[BPMNum].BPM, Time); + Time := 0; + end; // if + end; // if +end; + +function GetMidBeat(Time: real): real; +var + CurBeat: real; + CurBPM: integer; +// TopBeat: real; +// TempBeat: real; +// TempTime: real; +begin + Result := 0; + if Length(CurrentSong.BPM) = 1 then + Result := Time * CurrentSong.BPM[0].BPM / 60; + + (* 2 BPMs *) +{ if Length(CurrentSong.BPM) > 1 then begin + (* new system *) + CurBeat := 0; + TopBeat := GetBeats(CurrentSong.BPM[0].BPM, Time); + if TopBeat > CurrentSong.BPM[1].StartBeat then begin + // analyze second BPM + Time := Time - GetTimeForBeats(CurrentSong.BPM[0].BPM, CurrentSong.BPM[1].StartBeat - CurBeat); + CurBeat := CurrentSong.BPM[1].StartBeat; + TopBeat := GetBeats(CurrentSong.BPM[1].BPM, Time); + Result := CurBeat + TopBeat; + + end + else + begin + (* pierwszy przedzial *) + Result := TopBeat; + end; + end;} + + (* more BPMs *) + if Length(CurrentSong.BPM) > 1 then + begin + CurBeat := 0; + CurBPM := 0; + while (Time > 0) do + begin + GetMidBeatSub(CurBPM, Time, CurBeat); + Inc(CurBPM); + end; + + Result := CurBeat; + end; +end; + +function GetTimeFromBeat(Beat: integer): real; +var + CurBPM: integer; +begin + Result := 0; + if Length(CurrentSong.BPM) = 1 then + Result := CurrentSong.GAP / 1000 + Beat * 60 / CurrentSong.BPM[0].BPM; + + (* more BPMs *) + if Length(CurrentSong.BPM) > 1 then + begin + Result := CurrentSong.GAP / 1000; + CurBPM := 0; + while (CurBPM <= High(CurrentSong.BPM)) and + (Beat > CurrentSong.BPM[CurBPM].StartBeat) do + begin + if (CurBPM < High(CurrentSong.BPM)) and + (Beat >= CurrentSong.BPM[CurBPM+1].StartBeat) then + begin + // full range + Result := Result + (60 / CurrentSong.BPM[CurBPM].BPM) * + (CurrentSong.BPM[CurBPM+1].StartBeat - CurrentSong.BPM[CurBPM].StartBeat); + end; + + if (CurBPM = High(CurrentSong.BPM)) or + (Beat < CurrentSong.BPM[CurBPM+1].StartBeat) then + begin + // in the middle + Result := Result + (60 / CurrentSong.BPM[CurBPM].BPM) * + (Beat - CurrentSong.BPM[CurBPM].StartBeat); + end; + Inc(CurBPM); + end; + + { + while (Time > 0) do + begin + GetMidBeatSub(CurBPM, Time, CurBeat); + Inc(CurBPM); + end; + } + end; // if} +end; + +procedure Sing(Sender: TScreenSing); +var + Count: integer; + CountGr: integer; + CP: integer; + Done: real; + N: integer; +begin + LineState.CurrentTime := LineState.CurrentTime + TimeSkip; + + LineState.OldBeat := LineState.CurrentBeat; + LineState.MidBeat := GetMidBeat(LineState.CurrentTime - (CurrentSong.Gap{ + 90 I've forgotten for what it is}) / 1000); // new system with variable BPM in function + 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; + LineState.MidBeatD := -0.5+GetMidBeat(LineState.CurrentTime - (CurrentSong.Gap + 120 + 20) / 1000); // MidBeat with addition GAP + LineState.CurrentBeatD := Floor(LineState.MidBeatD); + LineState.FracBeatD := Frac(LineState.MidBeatD); + + // sentences routines + for CountGr := 0 to 0 do //High(Gracz) + begin; + CP := CountGr; + // ustawianie starej parts + LineState.OldLine := Lines[CP].Current; + + // wybieranie aktualnej parts + for Count := 0 to Lines[CP].High do + begin + if LineState.CurrentBeat >= Lines[CP].Line[Count].Start then + Lines[CP].Current := Count; + end; + + // czysczenie nut gracza, gdy to jest nowa plansza + // (optymizacja raz na halfbeat jest zla) + if Lines[CP].Current <> LineState.OldLine then + NewSentence(Sender); + + end; // for CountGr + + // wykonuje operacje raz na beat + if (LineState.CurrentBeat >= 0) and (LineState.OldBeat <> LineState.CurrentBeat) then + NewBeat(Sender); + + // make some operations on clicks + if {(LineState.CurrentBeatC >= 0) and }(LineState.OldBeatC <> LineState.CurrentBeatC) then + NewBeatC(Sender); + + // make some operations when detecting new voice pitch + if (LineState.CurrentBeatD >= 0) and (LineState.OldBeatD <> LineState.CurrentBeatD) then + NewBeatD(Sender); + + // wykonuje operacje w polowie beatu +// if (LineState.AktHalf >= 1) and (LineState.OldHalf <> LineState.AktHalf) then +// NewHalf; + + // plynnie przesuwa text + Done := 1; + for N := 0 to Lines[0].Line[Lines[0].Current].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 + 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; + + // wylacza ostatnia nute po przejsciu + {// 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 Sender.LyricMain.Selected := -1; + + if Done > 1 then Done := 1; + Sender.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; + end; + end;} + + +end; + +procedure NewSentence(Sender: TScreenSing); +var +G: Integer; +begin + // czyszczenie nut graczy + for G := 0 to High(Player) do + begin + Player[G].IlNut := 0; + Player[G].HighNote := -1; + SetLength(Player[G].Note, 0); + end; + + // Add Words to Lyrics + with Sender 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; + + //Sender.UpdateLCD; + + //On Sentence Change... + Sender.onSentenceChange(Lines[0].Current); +end; + +procedure NewBeat(Sender: TScreenSing); +var + Count: integer; +// TempBeat: integer; +begin + // ustawia zaznaczenie tekstu +// SingScreen.LyricMain.Selected := -1; + for Count := 0 to Lines[0].Line[Lines[0].Current].HighNote do + if (Lines[0].Line[Lines[0].Current].Note[Count].Start = LineState.CurrentBeat) then + begin + // operates on currently beated note + //Todo: Lyrics + //Sender.LyricMain.Selected := Count; + +// LCD.MoveCursor(1, ScreenSing.LyricMain.SelectedLetter); +// LCD.ShowCursor; + + //LCD.MoveCursorBR(Sender.LyricMain.SelectedLetter); + //LCD.ShowCursor; + end; +end; + +procedure NewBeatC; +var + Count: integer; +// LPT_1: integer; +// LPT_2: integer; +begin +// LPT_1 := 1; +// LPT_2 := 1; + + // beat click + if (Ini.BeatClick = 1) and ((LineState.CurrentBeatC + Lines[0].Resolution + Lines[0].NotesGAP) mod Lines[0].Resolution = 0) then + AudioPlayback.PlaySound(SoundLib.Click); + + // debug system on LPT + if ((LineState.CurrentBeatC + Lines[0].Resolution + Lines[0].NotesGAP) mod Lines[0].Resolution = 0) then + begin + //LPT_1 := 0; +// Light.LightOne(0, 150); + + (* + Light.LightOne(1, 200); // beat light + if ParamStr(1) = '-doublelights' then + Light.LightOne(0, 200); // beat light + *) + + +{ if ((LineState.CurrentBeatC + Lines[0].Resolution + Lines[0].NotesGAP) mod (Lines[0].Resolution * 2) = 0) then + Light.LightOne(0, 150) + else + Light.LightOne(1, 150)} + end; + + for Count := 0 to Lines[0].Line[Lines[0].Current].HighNote do + begin + if (Lines[0].Line[Lines[0].Current].Note[Count].Start = LineState.CurrentBeatC) then + begin + // click assist + if Ini.ClickAssist = 1 then + AudioPlayback.PlaySound(SoundLib.Click); + + //LPT_2 := 0; + (* + if ParamStr(1) <> '-doublelights' then + Light.LightOne(0, 150); //125 + *) + + // drum machine + (* + TempBeat := LineState.CurrentBeat;// + 2; + if (TempBeat mod 8 = 0) then Music.PlayDrum; + if (TempBeat mod 8 = 4) then Music.PlayClap; +// if (TempBeat mod 4 = 2) then Music.PlayHihat; + if (TempBeat mod 4 <> 0) then Music.PlayHihat; + *) + end; + end; + + {$IFDEF UseSerialPort} + // PortWriteB($378, LPT_1 + LPT_2 * 2); // 0 zapala + {$ENDIF} +end; + +procedure NewBeatD(Sender: TScreenSing); +begin + NewNote(Sender); +end; + +//procedure NewHalf; +//begin +// NewNote; +//end; + +procedure NewNote(Sender: TScreenSing); +var + CP: integer; // current player + S: integer; // sentence + SMin: integer; + SMax: integer; + SDet: integer; // temporary: sentence of detected note + Count: integer; + Mozna: boolean; + New: boolean; + Range: integer; + NoteHit:boolean; + MaxPoints: integer; // maximal points without line bonus +begin + // Log.LogStatus('Beat ' + IntToStr(LineState.CurrentBeat) + ' HalfBeat ' + IntToStr(LineState.AktHalf), 'NewBeat'); + + // On linux we get an AV @ NEWNOTE, line 600 of Classes/UMain.pas + if not assigned( AudioInputProcessor.Sound ) then + exit; + + // analizuje dla obu graczy ten sam sygnal (Sound.OneSrcForBoth) + // albo juz lepiej nie + for CP := 0 to PlayersPlay-1 do + begin + // analyze buffer + AudioInputProcessor.Sound[CP].AnalyzeBuffer; + + // adds some noise + //LineState.Tone := LineState.Tone + Round(Random(3)) - 1; + + // count min and max sentence range for checking (detection is delayed to the notes we see on the screen) + SMin := Lines[0].Current-1; + if SMin < 0 then + SMin := 0; + SMax := Lines[0].Current; + + // check if we can add new note + Mozna := false; + SDet:=SMin; + for S := SMin to SMax do + begin + for Count := 0 to Lines[0].Line[S].HighNote do + begin + if ((Lines[0].Line[S].Note[Count].Start <= LineState.CurrentBeatD) + and (Lines[0].Line[S].Note[Count].Start + Lines[0].Line[S].Note[Count].Length - 1 >= LineState.CurrentBeatD)) + and (Lines[0].Line[S].Note[Count].NoteType <> ntFreestyle) // but don't allow when it's FreeStyle note + and (Lines[0].Line[S].Note[Count].Length > 0) then // and make sure the note lengths is at least 1 + begin + SDet := S; + Mozna := true; + Break; + end; + end; + end; + + S := SDet; + + //Czas.SzczytJest := true; + //Czas.Tone := 27; + + // gdy moze, to dodaje nute - When Mozna, it adds note (?) + if (AudioInputProcessor.Sound[CP].ToneValid) and (Mozna) then + begin + // operowanie na ostatniej nucie + for Count := 0 to Lines[0].Line[S].HighNote do + begin + if (Lines[0].Line[S].Note[Count].Start <= LineState.OldBeatD+1) and + (Lines[0].Line[S].Note[Count].Start + + Lines[0].Line[S].Note[Count].Length > LineState.OldBeatD+1) then + begin + // to robi, tylko dla pary nut (oryginalnej i gracza) + + // przesuwanie tonu w odpowiednia game + while (AudioInputProcessor.Sound[CP].Tone - Lines[0].Line[S].Note[Count].Tone > 6) do + AudioInputProcessor.Sound[CP].Tone := AudioInputProcessor.Sound[CP].Tone - 12; + + while (AudioInputProcessor.Sound[CP].Tone - Lines[0].Line[S].Note[Count].Tone < -6) do + AudioInputProcessor.Sound[CP].Tone := AudioInputProcessor.Sound[CP].Tone + 12; + + // Half size Notes Patch + NoteHit := false; + + //if Ini.Difficulty = 0 then Range := 2; + //if Ini.Difficulty = 1 then Range := 1; + //if Ini.Difficulty = 2 then Range := 0; + Range := 2 - Ini.Difficulty; + + if abs(Lines[0].Line[S].Note[Count].Tone - AudioInputProcessor.Sound[CP].Tone) <= Range then + begin + AudioInputProcessor.Sound[CP].Tone := Lines[0].Line[S].Note[Count].Tone; + + // Half size Notes Patch + NoteHit := true; + + MaxPoints := 10000; + if (Ini.LineBonus <> 0) then + MaxPoints := 9000; + + case Lines[0].Line[S].Note[Count].NoteType of + ntNormal: Player[CP].Score := Player[CP].Score + MaxPoints / Lines[0].ScoreValue * + Lines[0].Line[S].Note[Count].Length; + ntGolden: Player[CP].ScoreGolden := Player[CP].ScoreGolden + MaxPoints / Lines[0].ScoreValue * + (Lines[0].Line[S].Note[Count].Length * 2); + end; + + Player[CP].ScoreI := Floor(Player[CP].Score / 10) * 10; + Player[CP].ScoreGoldenI := Floor(Player[CP].ScoreGolden / 10) * 10; + + Player[CP].ScoreTotalI := Player[CP].ScoreI + Player[CP].ScoreGoldenI + Player[CP].ScoreLineI; + end; + + end; // operowanie + end; // for + + // sprawdzanie czy to nowa nuta, czy przedluzenie + if S = SMax then + begin + New := true; + // if last has the same tone + if (Player[CP].IlNut > 0 ) and + (Player[CP].Note[Player[CP].HighNote].Tone = AudioInputProcessor.Sound[CP].Tone) and + (Player[CP].Note[Player[CP].HighNote].Start + Player[CP].Note[Player[CP].HighNote].Length = LineState.CurrentBeatD) then + begin + New := false; + end; + + // if is not as new note to control "beacie" (TODO: translate polish "beacie") + for Count := 0 to Lines[0].Line[S].HighNote do + begin + if (Lines[0].Line[S].Note[Count].Start = LineState.CurrentBeatD) then + New := true; + end; + + // dodawanie nowej nuty + if New then + begin + // New Note + Player[CP].IlNut := Player[CP].IlNut + 1; + Player[CP].HighNote := Player[CP].HighNote + 1; + SetLength(Player[CP].Note, Player[CP].IlNut); + Player[CP].Note[Player[CP].HighNote].Start := LineState.CurrentBeatD; + Player[CP].Note[Player[CP].HighNote].Length := 1; + Player[CP].Note[Player[CP].HighNote].Tone := AudioInputProcessor.Sound[CP].Tone; // Ton || TonDokl + Player[CP].Note[Player[CP].HighNote].Detekt := LineState.MidBeat; + + // Half Note Patch + Player[CP].Note[Player[CP].HighNote].Hit := NoteHit; + + //Log.LogStatus('New Note ' + IntToStr(Gracz.Note[Gracz.HighNote].Start), 'NewBeat'); + end + else + begin + // przedluzenie nuty + Player[CP].Note[Player[CP].HighNote].Length := Player[CP].Note[Player[CP].HighNote].Length + 1; + end; + + // check for perfect note and then lit the star (on Draw) + for Count := 0 to Lines[0].Line[S].HighNote do + begin + if (Lines[0].Line[S].Note[Count].Start = Player[CP].Note[Player[CP].HighNote].Start) and + (Lines[0].Line[S].Note[Count].Length = Player[CP].Note[Player[CP].HighNote].Length) and + (Lines[0].Line[S].Note[Count].Tone = Player[CP].Note[Player[CP].HighNote].Tone) then + begin + Player[CP].Note[Player[CP].HighNote].Perfect := true; + end; + end; + end; // if S = SMax + + end; // if moze + end; // for CP + // Log.LogStatus('EndBeat', 'NewBeat'); + + //On Sentence End -> For LineBonus + SingBar + if (sDet >= low(Lines[0].Line)) and (sDet <= high(Lines[0].Line)) then + begin + if assigned( Sender ) and + ((Lines[0].Line[SDet].Note[Lines[0].Line[SDet].HighNote].Start + Lines[0].Line[SDet].Note[Lines[0].Line[SDet].HighNote].Length - 1) = LineState.CurrentBeatD) then + begin + Sender.onSentenceEnd(sDet); + end; + end; + +end; + +procedure ClearScores(PlayerNum: integer); +begin + Player[PlayerNum].Score := 0; + Player[PlayerNum].ScoreI := 0; + Player[PlayerNum].ScoreLine := 0; + Player[PlayerNum].ScoreLineI := 0; + Player[PlayerNum].ScoreGolden := 0; + Player[PlayerNum].ScoreGoldenI := 0; + Player[PlayerNum].ScoreTotalI := 0; +end; + +//-------------------- +// Function sets all Absolute Paths e.g. Song Path and makes sure the Directorys exist +//-------------------- +procedure InitializePaths; + + // Initialize a Path Variable + // After Setting Paths, make sure that Paths exist + function initialize_path( out aPathVar : string; const aLocation : string ): boolean; + var + lWriteable: Boolean; + lAttrib : integer; + begin + lWriteable := false; + aPathVar := aLocation; + + // Make sure the directory is needex + ForceDirectories(aPathVar); + + if DirectoryExists(aPathVar) then + begin + lAttrib := fileGetAttr(aPathVar); + + lWriteable := (lAttrib and faDirectory <> 0) and + not (lAttrib and faReadOnly <> 0) + end; + + if not lWriteable then + Log.LogError('Error: Dir ('+ aLocation +') is Readonly'); + + result := lWriteable; + end; + +begin + initialize_path( LogPath , Platform.GetLogPath ); + initialize_path( SoundPath , Platform.GetGameSharedPath + 'Sounds' + PathDelim ); + initialize_path( ThemePath , Platform.GetGameSharedPath + 'Themes' + PathDelim ); + initialize_path( SkinsPath , Platform.GetGameSharedPath + 'Skins' + PathDelim ); + initialize_path( LanguagesPath , Platform.GetGameSharedPath + 'Languages' + PathDelim ); + initialize_path( PluginPath , Platform.GetGameSharedPath + 'Plugins' + PathDelim ); + initialize_path( VisualsPath , Platform.GetGameSharedPath + 'Visuals' + PathDelim ); + + initialize_path( ScreenshotsPath , Platform.GetGameUserPath + 'Screenshots' + PathDelim ); + + // Users Song Path .... + initialize_path( UserSongPath , Platform.GetGameUserPath + 'Songs' + PathDelim ); + initialize_path( UserCoversPath , Platform.GetGameUserPath + 'Covers' + PathDelim ); + initialize_path( UserPlaylistPath , Platform.GetGameUserPath + 'Playlists' + PathDelim ); + + // Shared Song Path .... + initialize_path( SongPath , Platform.GetGameSharedPath + 'Songs' + PathDelim ); + initialize_path( CoversPath , Platform.GetGameSharedPath + 'Covers' + PathDelim ); + initialize_path( PlaylistPath , Platform.GetGameSharedPath + 'Playlists' + PathDelim ); + + DecimalSeparator := '.'; +end; + +end. + diff --git a/Game/Code/Classes/UMusic.pas b/Game/Code/Classes/UMusic.pas index c4123dd0..c389574b 100644 --- a/Game/Code/Classes/UMusic.pas +++ b/Game/Code/Classes/UMusic.pas @@ -14,19 +14,9 @@ uses type TNoteType = (ntFreestyle, ntNormal, ntGolden); - //http://paste.ubuntu-nl.org/51892/ - - TMelody = record - Path: string; - Start: integer; // start of song in ms - IlNut: integer; // (TODO: Il = tone, Nut(a) = Note) - NoteLength: integer; - end; - PLine = ^TLine; TLine = record Start: integer; - StartNote: integer; Lyric: string; LyricWidth: real; End_: integer; @@ -40,10 +30,8 @@ type Start: integer; Length: integer; Tone: integer; // full range tone - ToneGamus: integer; // tone unified to one octave Text: string; - FreeStyle: boolean; - NoteType: integer; // normal-note: 1, golden-note: 2 + NoteType: TNoteType; end; end; ALine = array of TLine; // (TODO: rename to TLineArray) @@ -52,10 +40,10 @@ type TLines = record Current: integer; // for drawing of current line High: integer; - Number: integer; + Number: integer; Resolution: integer; NotesGAP: integer; - NoteType: integer; + ScoreValue: integer; Line: ALine; end; @@ -288,9 +276,6 @@ type end; var // TODO : JB --- THESE SHOULD NOT BE GLOBAL - // music - Melody: TMelody; - // czesci z nutami; Lines: array of TLines; diff --git a/Game/Code/Classes/USong.pas b/Game/Code/Classes/USong.pas index d6021811..60f5f0bf 100644 --- a/Game/Code/Classes/USong.pas +++ b/Game/Code/Classes/USong.pas @@ -126,23 +126,28 @@ type SongID: Integer; //ID of this Song in the Song Database FolderID: Integer; //ID of the Folder containing this Song FileName: String; //Filename of this Song w/o Path - FullPath: String; //Path + Filename - - Procedure ResetAttributes; virtual; //Reset all Attributes of this object - Procedure ReadHeader; virtual; //Reads Fileheader (Implemented by Child only) - Procedure ReadFile; virtual; //Reads complete File (Header + Notes) (Implemented by Child only) - Procedure WriteFile; virtual; //Writes complete File (Header + Notes) (Implemented by Child only) - Procedure ReadFromDB; virtual; //Reads all available Information from DB - Function CheckDB: Integer; virtual; //Checks DB for Song. Result > 0 SongExists (ID Returned) - Function UpdateDB: Integer; virtual;//Writes all Header Information set in this Song Object to DB. Returns ID (Required when Updated first Time) - public + FilePath: String; //Path of this Song + + Procedure ResetAttributes; virtual; //Reset all Attributes of this object + Procedure ReadHeader; virtual; abstract; //Reads Fileheader (Implemented by Child only) + Procedure ReadFile; virtual; abstract; //Reads complete File (Header + Notes) (Implemented by Child only) + Procedure WriteFile; virtual; abstract; //Writes complete File (Header + Notes) (Implemented by Child only) + Procedure ReadFromDB; virtual; //Reads all available Information from DB + Function CheckDB: Integer; virtual; //Checks DB for Song. Result > 0 SongExists (ID Returned) + Function UpdateDB: Integer; virtual; //Writes all Header Information set in this Song Object to DB. Returns ID (Required when Updated first Time) + + // procedures to manage the lyrics + Procedure ResetLyrics; + Procedure AddLyricLine(const PlayerID: Integer; const StartBeat: Integer; const RelativeBeat: Integer = -1); + Procedure AddNote(const PlayerID: Integer; const NoteType: Char; const NoteStart, NoteLength, NoteTone: Integer; const NoteText: WideString); + Function SolmizatLyrics(const NoteTone: Integer; const NoteText: WideString): WideString; + public //Required Information Title: widestring; Artist: widestring; Mp3: widestring; //Full Path to MP3 - Text: widestring; Creator: widestring; Resolution: integer; @@ -150,7 +155,6 @@ type GAP: real; // in miliseconds Base : array[0..1] of integer; - Rel : array[0..1] of integer; Mult : integer; MultBPM : integer; @@ -189,14 +193,16 @@ implementation uses TextGL, UIni, - UMusic, //needed for Lines - UMain; //needed for Player + UMusic; //needed for Lines + +var + RelativPosition: array [0..1] of Integer; constructor TSong.Create(const Path: String = ''; const FolderID: Integer = 0); begin If (Length(Path) > 0) AND (FolderID > 0) then begin //Read Song Infos from File or DB - FullPath := Path; + FilePath := ExtractFilePath(Path); FileName := ExtractFileName(Path); Self.FolderID := FolderID; SongID := CheckDB; @@ -249,35 +255,42 @@ end; //-------- Procedure TSong.ResetAttributes; begin - //to do -end; - -//-------- -// Reads Fileheader (Implemented by Child only) -//-------- -Procedure TSong.ReadHeader; -begin - //Implented in Childs only! + SongID := 0; + FolderID := 0; + FileName := ''; + FilePath := ''; + + Title := ''; + Artist := ''; + Mp3 := ''; + SetLength(BPM, 0); + + Creator := ''; + Resolution := 0; + GAP := 0; + + Base[0] := 100; + Base[1] := 100; + + Mult := 1; + MultBPM := 4; + + Cover := ''; + CoverID := 0; + Background := ''; + Video := ''; + VideoGAP := 0; + + NotesGAP := 0; + Start := 0; + Finish := 0; + Relative := False; + + Genre := ''; + Edition := ''; + Language := ''; end; -//-------- -// Reads complete File (Header + Notes) (Implemented by Child only) -//-------- -Procedure TSong.ReadFile; -begin - //Implented in Childs only! -end; - - -//-------- -// Writes complete File (Header + Notes) (Implemented by Child only) -//-------- -Procedure TSong.WriteFile; -begin - //Implented in Childs only! -end; - - //-------- // Reads all available Information from DB //-------- @@ -304,6 +317,154 @@ begin // to- do end; +Procedure TSong.ResetLyrics; +var + i: Integer; +begin + for i := 0 to High(Lines) do + begin + SetLength(Lines[i].Line, 0); + Lines[i].High := -1; + end; + + for i := 0 to High(RelativPosition) do + begin + RelativPosition[i] := 0; + end; +end; + +Procedure TSong.AddLyricLine(const PlayerID: Integer; const StartBeat: Integer; const RelativeBeat: Integer = -1); +var + NewLineIdx: Integer; +begin + NewLineIdx := High(Lines[PlayerID].Line) + 1; + + with Lines[PlayerID] do + begin + // recent added line is not the last of the song + if (NewLineIdx > 0) then + Lines[PlayerID].Line[NewLineIdx - 1].LastLine := False; + + // if last line, has no notes, ignore the new line (except for the RelativeBeat) + if (NewLineIdx < 1) OR (Lines[PlayerID].Line[NewLineIdx - 1].HighNote > -1) then + begin + // create lyric line and update references + SetLength(Line, NewLineIdx); + High := NewLineIdx; + Number := Number + 1; + with Line[High] do + begin + // default values + TotalNotes := 0; + HighNote := -1; + LastLine := True; + BaseNote := 100; + + // set start beat count of this new line + Start := (RelativPosition[PlayerID] + StartBeat) * Mult; + end; + end; + + if Relative then + begin + if RelativeBeat >= 0 then + // if this is a relativ song, we have to update the relativ offset + RelativPosition[PlayerID] := (RelativPosition[PlayerID] + RelativeBeat) * Mult + else + RelativPosition[PlayerID] := (RelativPosition[PlayerID] + StartBeat) * Mult; + end; + end; +end; + +Procedure TSong.AddNote(const PlayerID: Integer; const NoteType: Char; const NoteStart, NoteLength, NoteTone: Integer; const NoteText: WideString); +begin + if (High(Lines[PlayerID].Line) < 0) then + AddLyricLine(PlayerID, NoteStart, 0); + + with Lines[PlayerID].Line[Lines[PlayerID].High] do begin + // array of Notes expand to have space for new Note + HighNote := HighNote + 1; + TotalNotes := TotalNotes + 1; + SetLength(Note, HighNote + 1); + + with Note[HighNote] do + begin + Start := (RelativPosition[PlayerID] + NoteStart) * Mult; + Length := NoteLength * Mult; + Tone := NoteTone; + Text := SolmizatLyrics(NoteTone, NoteText); + Lyric := Lyric + Text; + + // identify lowest note of line + if Tone < BaseNote then + BaseNote := Tone; + end; + + case NoteType of + 'F': Note[HighNote].NoteType := ntFreestyle; + ':': Note[HighNote].NoteType := ntNormal; + '*': Note[HighNote].NoteType := ntGolden; + end; + + // calculate total score value + if (Note[HighNote].NoteType = ntNormal) then + begin + // normal notes + Lines[PlayerID].ScoreValue := Lines[PlayerID].ScoreValue + Note[HighNote].Length; + TotalNotes := TotalNotes + Note[HighNote].Length; + end + else if (Note[HighNote].NoteType = ntGolden) then + begin + // golden notes + Lines[PlayerID].ScoreValue := Lines[PlayerID].ScoreValue + (Note[HighNote].Length * 2); + TotalNotes := TotalNotes + (Note[HighNote].Length * 2); + end; + + // finish of the line + End_ := Note[HighNote].Start + Note[HighNote].Length; + end; // with +end; + +Function TSong.SolmizatLyrics(const NoteTone: Integer; const NoteText: WideString): WideString; +begin + Result := NoteText; + + case Ini.Solmization of + 1: // european + case (NoteTone mod 12) of + 0..1: Result := 'do '; + 2..3: Result := 're '; + 4: Result := 'mi '; + 5..6: Result := 'fa '; + 7..8: Result := 'sol '; + 9..10: Result := 'la '; + 11: Result := 'si '; + end; + + 2: // japanese + case (NoteTone mod 12) of + 0..1: Result := 'do '; + 2..3: Result := 're '; + 4: Result := 'mi '; + 5..6: Result := 'fa '; + 7..8: Result := 'so '; + 9..10: Result := 'la '; + 11: Result := 'shi '; + end; + + 3: // american + case (NoteTone mod 12) of + 0..1: Result := 'do '; + 2..3: Result := 're '; + 4: Result := 'mi '; + 5..6: Result := 'fa '; + 7..8: Result := 'sol '; + 9..10: Result := 'la '; + 11: Result := 'ti '; + end; + end; // case Ini.Solmization +end; + {constructor TSong.create( const aFileName : WideString ); begin @@ -998,126 +1159,6 @@ begin end; -procedure TSong.ParseNote(LineNumber: integer; TypeP: char; StartP, DurationP, NoteP: integer; LyricS: string); -//var -// Space: boolean; // Auto Removed, Unused Variable -begin - case Ini.Solmization of - 1: // european - begin - case (NoteP mod 12) of - 0..1: LyricS := ' do '; - 2..3: LyricS := ' re '; - 4: LyricS := ' mi '; - 5..6: LyricS := ' fa '; - 7..8: LyricS := ' sol '; - 9..10: LyricS := ' la '; - 11: LyricS := ' si '; - end; - end; - 2: // japanese - begin - case (NoteP mod 12) of - 0..1: LyricS := ' do '; - 2..3: LyricS := ' re '; - 4: LyricS := ' mi '; - 5..6: LyricS := ' fa '; - 7..8: LyricS := ' so '; - 9..10: LyricS := ' la '; - 11: LyricS := ' shi '; - end; - end; - 3: // american - begin - case (NoteP mod 12) of - 0..1: LyricS := ' do '; - 2..3: LyricS := ' re '; - 4: LyricS := ' mi '; - 5..6: LyricS := ' fa '; - 7..8: LyricS := ' sol '; - 9..10: LyricS := ' la '; - 11: LyricS := ' ti '; - end; - end; - end; // case - - with Lines[LineNumber].Line[Lines[LineNumber].High] do begin - SetLength(Note, Length(Note) + 1); - IlNut := IlNut + 1; - HighNote := HighNote + 1; - Melody.IlNut := Melody.IlNut + 1; - - Note[HighNote].Start := StartP; - if IlNut = 1 then begin - StartNote := Note[HighNote].Start; - if Lines[LineNumber].Number = 1 then - Start := -100; -// Start := Note[HighNote].Start; - end; - - Note[HighNote].Length := DurationP; - Melody.NoteLength := Melody.NoteLength + Note[HighNote].Length; - - // back to the normal system with normal, golden and now freestyle notes - case TypeP of - 'F': Note[HighNote].NoteType := 0; - ':': Note[HighNote].NoteType := 1; - '*': Note[HighNote].NoteType := 2; - end; - - Lines[LineNumber].NoteType := Lines[LineNumber].NoteType + Note[HighNote].Length * Note[HighNote].NoteType; - - Note[HighNote].Tone := NoteP; - if Note[HighNote].Tone < Base[LineNumber] then Base[LineNumber] := Note[HighNote].Tone; - Note[HighNote].ToneGamus := Note[HighNote].ToneGamus mod 12; - - Note[HighNote].Text := Copy(LyricS, 2, 100); - Lyric := Lyric + Note[HighNote].Text; - - if TypeP = 'F' then - Note[HighNote].FreeStyle := true; - - End_ := Note[HighNote].Start + Note[HighNote].Length; - end; // with -end; - -procedure TSong.NewSentence(LineNumberP: integer; Param1, Param2: integer); -var -I: Integer; -begin - - // stara czesc //Alter Satz //Update Old Part - Lines[LineNumberP].Line[Lines[LineNumberP].High].BaseNote := Base[LineNumberP]; - Lines[LineNumberP].Line[Lines[LineNumberP].High].LyricWidth := glTextWidth(PChar(Lines[LineNumberP].Line[Lines[LineNumberP].High].Lyric)); - - //Total Notes Patch - Lines[LineNumberP].Line[Lines[LineNumberP].High].TotalNotes := 0; - for I := low(Lines[LineNumberP].Line[Lines[LineNumberP].High].Note) to high(Lines[LineNumberP].Line[Lines[LineNumberP].High].Note) do - begin - Lines[LineNumberP].Line[Lines[LineNumberP].High].TotalNotes := Lines[LineNumberP].Line[Lines[LineNumberP].High].TotalNotes + Lines[LineNumberP].Line[Lines[LineNumberP].High].Note[I].Length * Lines[LineNumberP].Line[Lines[LineNumberP].High].Note[I].NoteType; - end; - //Total Notes Patch End - - - // nowa czesc //Neuer Satz //Update New Part - SetLength(Lines[LineNumberP].Line, Lines[LineNumberP].Number + 1); - Lines[LineNumberP].High := Lines[LineNumberP].High + 1; - Lines[LineNumberP].Number := Lines[LineNumberP].Number + 1; - Lines[LineNumberP].Line[Lines[LineNumberP].High].HighNote := -1; - - if self.Relative then - begin - Lines[LineNumberP].Line[Lines[LineNumberP].High].Start := Param1; - Rel[LineNumberP] := Rel[LineNumberP] + Param2; - end - else - Lines[LineNumberP].Line[Lines[LineNumberP].High].Start := Param1; - - Lines[LineNumberP].Line[Lines[LineNumberP].High].LastLine := False; - - Base[LineNumberP] := 100; // high number -end; - procedure TSong.clear(); begin //Main Information diff --git a/Game/Code/Classes/USong_TextFile.pas b/Game/Code/Classes/USong_TextFile.pas new file mode 100644 index 00000000..a3e605de --- /dev/null +++ b/Game/Code/Classes/USong_TextFile.pas @@ -0,0 +1,86 @@ +unit USong_TextFile; + +interface + +{$IFDEF FPC} + {$MODE Delphi} +{$ENDIF} + +{$I switches.inc} + +uses + Classes, + SysUtils, + USong; + +type + {******************* + Child of the new TSong class. + implements filehandling to load a song from a text file + *******************} + TSong_TextFile = class(TSong) + protected + SongFile: TextFile; + + Function OpenSongFile: Boolean; + Function IsDataAvailable: Boolean; + Function GetNextLine(): String; + Procedure CloseSongFile; + end; + +implementation + +uses + ULog; + +//-------- +// Open the SongFile +//-------- +Function TSong_TextFile.OpenSongFile: Boolean; +begin + Result := False; + + if not FileExists(FilePath + FileName) then + Log.LogError('File does not exsist', FilePath + FileName) + else + begin + try + AssignFile(SongFile, FilePath + FileName); + Reset(SongFile); + Result := True; + except + Log.LogError('Faild to open file', FilePath + FileName) + end; + end; +end; + +//-------- +// More data in songfile available? +//-------- +Function TSong_TextFile.IsDataAvailable: Boolean; +begin + Result := not eof(SongFile); +end; + +//-------- +// Returns the next line from the SongFile +//-------- +Function TSong_TextFile.GetNextLine(): String; +begin + ReadLn(SongFile, Result); + Result := Trim(Result); +end; + +//-------- +// Close the SongFile +//-------- +Procedure TSong_TextFile.CloseSongFile; +begin + try + CloseFile(SongFile); + except + Log.LogError('Error closing file', FilePath + FileName); + end; +end; + +end. \ No newline at end of file diff --git a/Game/Code/Classes/USong_Txt.pas b/Game/Code/Classes/USong_Txt.pas new file mode 100644 index 00000000..23171517 --- /dev/null +++ b/Game/Code/Classes/USong_Txt.pas @@ -0,0 +1,438 @@ +unit USong_Txt; + +interface + +{$IFDEF FPC} + {$MODE Delphi} +{$ENDIF} + +{$I switches.inc} + + +uses + Classes, + SysUtils, + USong_TextFile; + +type + {******************* + Child of the new TSong class. + implements methods to load a song form a txt file (ultrastar file format) + *******************} + TSong_Txt = class(TSong_TextFile) + public + Procedure ReadHeader; override; //Reads Fileheader (Implemented by Child only) + Procedure ReadFile; override; //Reads complete File (Header + Notes) (Implemented by Child only) + Procedure WriteFile; override; //Writes complete File (Header + Notes) (Implemented by Child only) + end; + +implementation + +uses + UMusic, + UMain, + UPlatform, + ULog; + +type + TTags = (ArtistTag, TitleTag, Mp3Tag, BPMTag); + +//-------- +// Reads Fileheader +//-------- +Procedure TSong_Txt.ReadHeader; +var + started: Boolean; + line, key, value: String; + FloatValue: Real; + FoundTags: Set of TTags; + + // splits a headerline in key an value + // returns true on success and false if this is not a valid headerline + Function SplitHeaderLine(const Line: String; var Key, Value: String): Boolean; + var + idx: Integer; + begin + Result := False; + + idx := Pos(':', Line); + if idx > 0 then + begin + Key := Uppercase(Trim(Copy(Line, 2, idx - 2))); //Uppercase is for Case Insensitive Checks + Value := Trim(Copy(Line, idx + 1,Length(Line) - idx)); + + if (Length(Key) > 0) AND (Length(Value) > 0) then + Result := True + end; + end; + + function song_StrToFloat(const aValue : String): Extended; + var + lValue: String; + begin + lValue := aValue; + + if (Pos(',', lValue) <> 0) then + lValue[Pos(',', lValue)] := '.'; + + Result := StrToFloatDef(lValue, 0); + end; + +begin + if not OpenSongFile then + exit; + + started := False; + FoundTags := []; + + while isDataAvailable do + begin + line := GetNextLine(); + + // break if header has finished + if started AND ((Length(line) > 0) AND (not (line[1] = '#'))) then + break; + + // skip invalid lines at beginning + if (not started) AND (line[1] = '#') then + started := True; + + // parse line + if started then + begin + if (Length(line) > 0) AND not SplitHeaderLine(line, key, value) then + begin + Log.LogError('Invalide line in Header of file: ' + FilePath + FileName); + Log.LogError('Line: ' + line); + break; + end; + + {$IFDEF UTF8_FILENAMES} + if ((Key = 'MP3') or (Key = 'BACKGROUND') or (Key = 'COVER') or (Key = 'VIDEO')) then + Value := Utf8Encode(Value); + {$ENDIF} + + //Title + if (Key = 'TITLE') then + begin + Title := Value; + FoundTags := FoundTags + [TitleTag]; + continue; + end; + + //Artist + if (Key = 'ARTIST') then + begin + Artist := Value; + FoundTags := FoundTags + [ArtistTag]; + continue; + end; + + //MP3 File //Test if Exists + if (Key = 'MP3') then + begin + if FileExists(FilePath + Value) then + begin + Mp3 := FilePath + Value; + FoundTags := FoundTags + [Mp3Tag]; + end + else + Log.LogError('Can''t find MP3 File: ' + FilePath + Value + ' in Song: ' + FilePath + FileName); + continue; + end; + + //Beats per Minute + if (Key = 'BPM') then + begin + FloatValue := song_StrtoFloat( Value ) * Mult * MultBPM; + + if FloatValue <> 0 then + begin + SetLength(BPM, 1); + BPM[0].StartBeat := 0; + BPM[0].BPM := floatValue; + FoundTags := FoundTags + [BPMTag]; + end; + + continue; + end; + + //--------- + //Additional Header Information + //--------- + + // Gap + if (Key = 'GAP') then + begin + GAP := song_StrtoFloat( Value ); + + continue; + end; + + //Cover Picture + if (Key = 'COVER') then + begin + if FileExists(FilePath + Value) then + Cover := FilePath + Value + else + Log.LogError('Can''t find Cover File: ' + FilePath + Value + ' in Song: ' + FilePath + FileName); + + continue; + end; + + //Background Picture + if (Key = 'BACKGROUND') then + begin + if FileExists(FilePath + Value) then + Background := FilePath + Value + else + Log.LogError('Can''t find Background File: ' + FilePath + Value + ' in Song: ' + FilePath + FileName); + + continue; + end; + + // Video File + if (Key = 'VIDEO') then + begin + if FileExists(FilePath + Value) then + Video := FilePath + Value + else + Log.LogError('Can''t find Video File: ' + FilePath + Value + ' in Song: ' + FilePath + FileName); + + continue; + end; + + // Video Gap + if (Key = 'VIDEOGAP') then + begin + VideoGAP := song_StrtoFloat(Value); + continue; + end; + + //Genre Sorting + if (Key = 'GENRE') then + begin + Genre := Value; + continue; + end; + + //Edition Sorting + if (Key = 'EDITION') then + begin + Edition := Value; + continue; + end; + + //Creator Tag + if (Key = 'CREATOR') then + begin + Creator := Value; + continue; + end; + + //Language Sorting + if (Key = 'LANGUAGE') then + begin + Language := Value; + continue; + end; + + // Song Start + if (Key = 'START') then + begin + Start := song_StrtoFloat( Value ); + continue; + end; + + // Song Ending + if (Key = 'END') then + begin + TryStrtoInt(Value, Finish); + continue; + end; + + // Resolution + if (Key = 'RESOLUTION') then + begin + TryStrtoInt(Value, Resolution); + continue; + end; + + // Notes Gap + if (Key = 'NOTESGAP') then + begin + TryStrtoInt(Value, NotesGAP); + continue; + end; + + // Relative Notes + if (Key = 'RELATIVE') AND (uppercase(Value) = 'YES') then + begin + Relative := True; + continue; + end; + + end; // if started + end; // while + + if Cover = '' then + begin + Cover := platform.FindSongFile(FilePath, '*[CO].jpg'); + end; + + // check if all required infos are given + if not (BPMTag in FoundTags) then + Log.LogError('BPM Tag missing: ' + FilePath + FileName); + + if not (MP3Tag in FoundTags) then + Log.LogError('MP3 Tag missing or invalid file: ' + FilePath + FileName); + + if not (ArtistTag in FoundTags) then + Log.LogError('Artist Tag missing: ' + FilePath + FileName); + + if not (TitleTag in FoundTags) then + Log.LogError('Title Tag missing: ' + FilePath + FileName); + + CloseSongFile(); +end; + +//-------- +// Reads complete File (Header + Notes) +//-------- +Procedure TSong_Txt.ReadFile; +var + Line: String; + NotesRead: Boolean; + Values: TStringList; + + Procedure ParseDelimited(const StringList: TStringList; const Value: String; const Delimiter: String; const MaxParts: Integer); + var + idx: Integer; + Source: String; + Delta: Integer; + begin + Delta := Length(Delimiter); + Source := Value; + StringList.BeginUpdate; + StringList.Clear; + try + while ((Pos(Delimiter, Source) > 0) OR ((MaxParts > 0) AND (StringList.count+1 >= MaxParts))) do + begin + idx := Pos(Delimiter, Source); + StringList.Add(Copy(Source,0,idx-1)); + Source := Copy(Source,idx+Delta, MaxInt); + end; + + if (Length(Source) > 0) then + StringList.Add(Source); + finally + StringList.EndUpdate; + end; + end; + +begin + // read Header + Self.ReadHeader; + + OpenSongFile(); + + ResetLyrics; + NotesRead := False; + + while isDataAvailable do + begin + line := GetNextLine(); + + // end of song + if (line[1] = 'E') then + break; + + // skip invalid lines + if (line[1] <> ':') OR (line[1] <> 'F') OR (line[1] <> '*') OR (line[1] <> '-') OR (line[1] <> 'B') then + continue; + + // aktuelle Zeile in einzelne Werte aufteilen + Values := TStringList.Create; + ParseDelimited(Values, line, ' ', 5); + + try + if (line[1] = '-') then + if (not NotesRead) then + // skip newline if no notes before + continue + else + begin + // new lyric line + // param count: 1 (relative: 2) + + if (Values.Count > 2) then + // relativ offset + begin + // P1 + AddLyricLine(0, StrToInt(Values[1]), StrToInt(Values[2])); + + // P2 + if Length(Player) = 2 Then + AddLyricLine(1, StrToInt(Values[1]), StrToInt(Values[2])) + end + else + begin + AddLyricLine(0, StrToInt(Values[1])); + + // P2 + if Length(Player) = 2 then + AddLyricLine(1, StrToInt(Values[1])) + end; + end; + + // new BPM set + if (line[1] = 'B') then + begin + // param count: 2 + {SetLength(BPM, Length(BPM) + 1); + Read(SongFile, BPM[High(BPM)].StartBeat); + BPM[High(BPM)].StartBeat := BPM[High(BPM)].StartBeat + Rel[0]; + + ReadLn(SongFile, Text); + BPM[High(BPM)].BPM := StrToFloat(Text); + BPM[High(BPM)].BPM := BPM[High(BPM)].BPM * Mult * MultBPM;} + end; + + if (line[1] = ':') OR (line[1] = '*') OR (line[1] = 'F') then + begin + // param count: 4 + if (Values.Count < 5) then + Log.LogError('Error parsing line: ' + line, 'Not enough arguments.') + else + begin + // Check for ZeroNote + if StrToInt(Values[2]) = 0 then + Log.LogError('Found ZeroNote: "' + line + '" -> Note ignored!') + else + begin + // P1 + AddNote(0, line[1], StrToInt(Values[1]), StrToInt(Values[2]), StrToInt(Values[3]), Values[4]); + + // P2 + if Length(Player) = 2 then + AddNote(1, line[1], StrToInt(Values[1]), StrToInt(Values[2]), StrToInt(Values[3]), Values[4]); + end; + end; + end; + except + on E : Exception do + Log.LogError('Error parsing line: ' + line, E.ClassName + ': ' + E.Message); + end; + end; + + CloseSongFile(); +end; + +//-------- +// Writes complete File (Header + Notes) +//-------- +Procedure TSong_Txt.WriteFile; +begin +end; + +end. \ No newline at end of file -- cgit v1.2.3