diff options
Diffstat (limited to 'Game/Code/Classes')
-rw-r--r-- | Game/Code/Classes/UDraw.pas | 48 | ||||
-rw-r--r-- | Game/Code/Classes/UFiles.pas | 6 | ||||
-rw-r--r-- | Game/Code/Classes/ULyrics.pas | 47 | ||||
-rw-r--r-- | Game/Code/Classes/ULyrics_bak.pas | 2 | ||||
-rw-r--r-- | Game/Code/Classes/UMain.pas | 2253 | ||||
-rw-r--r-- | Game/Code/Classes/UMusic.pas | 21 | ||||
-rw-r--r-- | Game/Code/Classes/USong.pas | 365 | ||||
-rw-r--r-- | Game/Code/Classes/USong_TextFile.pas | 86 | ||||
-rw-r--r-- | Game/Code/Classes/USong_Txt.pas | 438 |
9 files changed, 1903 insertions, 1363 deletions
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,36 +255,43 @@ 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 //-------- Procedure TSong.ReadFromDB; @@ -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 |