aboutsummaryrefslogtreecommitdiffstats
path: root/src/screens/UScreenSing.pas
diff options
context:
space:
mode:
authork-m_schindler <k-m_schindler@b956fd51-792f-4845-bead-9b4dfca2ff2c>2008-08-27 14:58:32 +0000
committerk-m_schindler <k-m_schindler@b956fd51-792f-4845-bead-9b4dfca2ff2c>2008-08-27 14:58:32 +0000
commit873f177f08dc7c4fe2d7e50bbe7709df98e238d3 (patch)
tree78158db79e85a1ac406e81cd6923af4b5a0fd0bc /src/screens/UScreenSing.pas
parent6809e61aef42c36bfebe16414693c82e45bceaff (diff)
downloadusdx-873f177f08dc7c4fe2d7e50bbe7709df98e238d3.tar.gz
usdx-873f177f08dc7c4fe2d7e50bbe7709df98e238d3.tar.xz
usdx-873f177f08dc7c4fe2d7e50bbe7709df98e238d3.zip
rename Screen part2
git-svn-id: svn://svn.code.sf.net/p/ultrastardx/svn/trunk@1306 b956fd51-792f-4845-bead-9b4dfca2ff2c
Diffstat (limited to 'src/screens/UScreenSing.pas')
-rw-r--r--src/screens/UScreenSing.pas934
1 files changed, 934 insertions, 0 deletions
diff --git a/src/screens/UScreenSing.pas b/src/screens/UScreenSing.pas
new file mode 100644
index 00000000..911d122e
--- /dev/null
+++ b/src/screens/UScreenSing.pas
@@ -0,0 +1,934 @@
+unit UScreenSing;
+
+interface
+
+{$IFDEF FPC}
+ {$MODE Delphi}
+{$ENDIF}
+
+{$I switches.inc}
+
+
+uses UMenu,
+ UMusic,
+ SDL,
+ SysUtils,
+ UFiles,
+ UTime,
+ USongs,
+ UIni,
+ ULog,
+ UTexture,
+ ULyrics,
+ TextGL,
+ gl,
+ UThemes,
+ UGraphicClasses,
+ USingScores;
+
+type
+ TLyricsSyncSource = class(TSyncSource)
+ function GetClock(): real; override;
+ end;
+
+type
+ TScreenSing = class(TMenu)
+ protected
+ Paused: boolean; //Pause Mod
+ LyricsSync: TLyricsSyncSource;
+ NumEmptySentences: integer;
+ public
+ //TextTime: integer;
+
+ // TimeBar fields
+ StaticTimeProgress: integer;
+ TextTimeText: integer;
+
+ StaticP1: integer;
+ TextP1: integer;
+
+ //shown when game is in 2/4 player modus
+ StaticP1TwoP: integer;
+ TextP1TwoP: integer;
+
+ //shown when game is in 3/6 player modus
+ StaticP1ThreeP: integer;
+ TextP1ThreeP: integer;
+
+ StaticP2R: integer;
+ TextP2R: integer;
+
+ StaticP2M: integer;
+ TextP2M: integer;
+
+ StaticP3R: integer;
+ TextP3R: integer;
+
+ StaticPausePopup: integer;
+
+ Tex_Background: TTexture;
+ FadeOut: boolean;
+ Lyrics: TLyricEngine;
+
+ //Score Manager:
+ Scores: TSingScores;
+
+ fShowVisualization: boolean;
+ fCurrentVideoPlaybackEngine: IVideoPlayback;
+
+ constructor Create; override;
+ procedure onShow; override;
+ procedure onShowFinish; override;
+
+ function ParseInput(PressedKey: cardinal; CharCode: widechar;
+ PressedDown: boolean): boolean; override;
+ function Draw: boolean; override;
+
+ procedure Finish; virtual;
+ procedure Pause; // Toggle Pause
+
+ procedure OnSentenceEnd(SentenceIndex: cardinal); // for LineBonus + Singbar
+ procedure OnSentenceChange(SentenceIndex: cardinal); // for Golden Notes
+ end;
+
+implementation
+
+uses UGraphic,
+ UDraw,
+ UMain,
+ USong,
+ Classes,
+ URecord,
+ ULanguage,
+ Math;
+
+ // Method for input parsing. If False is returned, GetNextWindow
+ // should be checked to know the next window to load;
+function TScreenSing.ParseInput(PressedKey: cardinal; CharCode: widechar;
+ PressedDown: boolean): boolean;
+begin
+ Result := True;
+ if (PressedDown) then
+ begin // Key Down
+ // check normal keys
+ case WideCharUpperCase(CharCode)[1] of
+ 'Q':
+ begin
+ //When not ask before Exit then Finish now
+ if (Ini.AskbeforeDel <> 1) then
+ Finish
+ //else just Pause and let the Popup make the Work
+ else if not Paused then
+ Pause;
+
+ Result := False;
+ Exit;
+ end;
+ 'V': //Show Visualization
+ begin
+ fShowVisualization := not fShowVisualization;
+
+ if fShowVisualization then
+ fCurrentVideoPlaybackEngine := Visualization
+ else
+ fCurrentVideoPlaybackEngine := VideoPlayback;
+
+ if fShowVisualization then
+ fCurrentVideoPlaybackEngine.play;
+
+ Exit;
+ end;
+ 'P':
+ begin
+ Pause;
+ Exit;
+ end;
+ end;
+
+ // check special keys
+ case PressedKey of
+ SDLK_ESCAPE,
+ SDLK_BACKSPACE:
+ begin
+ //Record Sound Hack:
+ //Sound[0].BufferLong
+
+ Finish;
+ AudioPlayback.PlaySound(SoundLib.Back);
+ FadeTo(@ScreenScore);
+ end;
+
+ SDLK_SPACE:
+ begin
+ Pause;
+ end;
+
+ SDLK_TAB: //Change Visualization Preset
+ begin
+ if fShowVisualization then
+ fCurrentVideoPlaybackEngine.Position := now; // move to a random position
+ end;
+
+ SDLK_RETURN:
+ begin
+ end;
+
+ // Up and Down could be done at the same time,
+ // but I don't want to declare variables inside
+ // functions like this one, called so many times
+ SDLK_DOWN:
+ begin
+ end;
+ SDLK_UP:
+ begin
+ end;
+ end;
+ end;
+end;
+
+//Pause Mod
+procedure TScreenSing.Pause;
+begin
+ if (not Paused) then //enable Pause
+ begin
+ // pause Time
+ Paused := True;
+
+ LyricsState.Pause();
+
+ // pause Music
+ AudioPlayback.Pause;
+
+ // pause Video
+ if (CurrentSong.Video <> '') and FileExists(CurrentSong.Path +
+ CurrentSong.Video) then
+ fCurrentVideoPlaybackEngine.Pause;
+
+ end
+ else //disable Pause
+ begin
+ LyricsState.Resume();
+
+ // Play Music
+ AudioPlayback.Play;
+
+ // Video
+ if (CurrentSong.Video <> '') and FileExists(CurrentSong.Path +
+ CurrentSong.Video) then
+ fCurrentVideoPlaybackEngine.Pause;
+
+ Paused := False;
+ end;
+end;
+//Pause Mod End
+
+constructor TScreenSing.Create;
+var
+ I: integer;
+ P: integer;
+begin
+ inherited Create;
+
+ fShowVisualization := False;
+
+ fCurrentVideoPlaybackEngine := VideoPlayback;
+
+ //Create Score Class
+ Scores := TSingScores.Create;
+ Scores.LoadfromTheme;
+
+ LoadFromTheme(Theme.Sing);
+
+ //TimeBar
+ StaticTimeProgress := AddStatic(Theme.Sing.StaticTimeProgress);
+ TextTimeText := AddText(Theme.Sing.TextTimeText);
+
+ // 1 player | P1
+ StaticP1 := AddStatic(Theme.Sing.StaticP1);
+ TextP1 := AddText(Theme.Sing.TextP1);
+
+ // 2 or 4 players | P1
+ StaticP1TwoP := AddStatic(Theme.Sing.StaticP1TwoP);
+ TextP1TwoP := AddText(Theme.Sing.TextP1TwoP);
+
+ // | P2
+ StaticP2R := AddStatic(Theme.Sing.StaticP2R);
+ TextP2R := AddText(Theme.Sing.TextP2R);
+
+ // 3 or 6 players | P1
+ StaticP1ThreeP := AddStatic(Theme.Sing.StaticP1ThreeP);
+ TextP1ThreeP := AddText(Theme.Sing.TextP1ThreeP);
+
+ // | P2
+ StaticP2M := AddStatic(Theme.Sing.StaticP2M);
+ TextP2M := AddText(Theme.Sing.TextP2M);
+
+ // | P3
+ StaticP3R := AddStatic(Theme.Sing.StaticP3R);
+ TextP3R := AddText(Theme.Sing.TextP3R);
+
+ StaticPausePopup := AddStatic(Theme.Sing.PausePopUp);
+
+ //<note>Pausepopup is not visibile at the beginning</note>
+ Static[StaticPausePopup].Visible := False;
+
+ Lyrics := TLyricEngine.Create(80, Skin_LyricsT, 640, 12, 80, Skin_LyricsT + 36, 640, 12);
+
+ LyricsSync := TLyricsSyncSource.Create();
+end;
+
+procedure TScreenSing.onShow;
+var
+ P: integer;
+ V1: boolean;
+ V1TwoP: boolean; //Position of ScoreBox in two-player mode
+ V1ThreeP: boolean; //Position of ScoreBox in three-player mode
+ V2R: boolean;
+ V2M: boolean;
+ V3R: boolean;
+ NR: TRecR; //Some enlightment of who, how and what this is here please
+ Color: TRGB;
+
+ success: boolean;
+begin
+ inherited;
+
+ Log.LogStatus('Begin', 'onShow');
+ FadeOut := False;
+
+ // reset video playback engine, to play Video Clip...
+ fCurrentVideoPlaybackEngine := VideoPlayback;
+
+ // setup score manager
+ Scores.ClearPlayers; // clear old player values
+ Color.R := 0;
+ Color.G := 0;
+ Color.B := 0; // dummy atm <- \(O.o)/? B like bummy?
+
+ // add new players
+ for P := 0 to PlayersPlay - 1 do
+ begin
+ Scores.AddPlayer(Tex_ScoreBG[P], Color);
+ end;
+
+ Scores.Init; //Get Positions for Players
+
+ // prepare players
+ SetLength(Player, PlayersPlay);
+
+ case PlayersPlay of
+ 1:
+ begin
+ V1 := True;
+ V1TwoP := False;
+ V1ThreeP := False;
+ V2R := False;
+ V2M := False;
+ V3R := False;
+ end;
+ 2:
+ begin
+ V1 := False;
+ V1TwoP := True;
+ V1ThreeP := False;
+ V2R := True;
+ V2M := False;
+ V3R := False;
+ end;
+ 3:
+ begin
+ V1 := False;
+ V1TwoP := False;
+ V1ThreeP := True;
+ V2R := False;
+ V2M := True;
+ V3R := True;
+ end;
+ 4:
+ begin // double screen
+ V1 := False;
+ V1TwoP := True;
+ V1ThreeP := False;
+ V2R := True;
+ V2M := False;
+ V3R := False;
+ end;
+ 6:
+ begin // double screen
+ V1 := False;
+ V1TwoP := False;
+ V1ThreeP := True;
+ V2R := False;
+ V2M := True;
+ V3R := True;
+ end;
+
+ end;
+
+ //This one is shown in 1P mode
+ Static[StaticP1].Visible := V1;
+ Text[TextP1].Visible := V1;
+
+
+ //This one is shown in 2/4P mode
+ Static[StaticP1TwoP].Visible := V1TwoP;
+ Text[TextP1TwoP].Visible := V1TwoP;
+
+ Static[StaticP2R].Visible := V2R;
+ Text[TextP2R].Visible := V2R;
+
+
+ //This one is shown in 3/6P mode
+ Static[StaticP1ThreeP].Visible := V1ThreeP;
+ Text[TextP1ThreeP].Visible := V1ThreeP;
+
+
+ Static[StaticP2M].Visible := V2M;
+ Text[TextP2M].Visible := V2M;
+
+
+ Static[StaticP3R].Visible := V3R;
+ Text[TextP3R].Visible := V3R;
+
+
+ // FIXME: sets Path and Filename to ''
+ ResetSingTemp;
+
+ CurrentSong := CatSongs.Song[CatSongs.Selected];
+
+ // FIXME: bad style, put the try-except into LoadSong() and not here
+ try
+ // Check if file is XML
+ if copy(CurrentSong.FileName, length(CurrentSong.FileName) - 3, 4) = '.xml' then
+ success := CurrentSong.LoadXMLSong()
+ else
+ success := CurrentSong.LoadSong();
+ except
+ success := False;
+ end;
+
+ if (not success) then
+ begin
+ // error loading song -> go back to song screen and show some error message
+ FadeTo(@ScreenSong);
+ // select new song in party mode
+ if ScreenSong.Mode = smPartyMode then
+ ScreenSong.SelectRandomSong();
+ ScreenPopupError.ShowPopup(Language.Translate('ERROR_CORRUPT_SONG'));
+ // FIXME: do we need this?
+ CurrentSong.Path := CatSongs.Song[CatSongs.Selected].Path;
+ Exit;
+ end;
+
+ // reset video playback engine, to play video clip...
+ fCurrentVideoPlaybackEngine.Close;
+ fCurrentVideoPlaybackEngine := VideoPlayback;
+{**
+ * == Background ==
+ * We have four types of backgrounds:
+ * + Blank : Nothing has been set, this is our fallback
+ * + Picture : Picture has been set, and exists - otherwise we fallback
+ * + Video : Video has been set, and exists - otherwise we fallback
+ * + Visualization: + Off : No Visialization
+ * + WhenNoVideo: Overwrites Blank and Picture
+ * + On : Overwrites Blank, Picture and Video
+ *}
+{**
+ * set background to: video
+ *}
+ CurrentSong.VideoLoaded := False;
+ fShowVisualization := False;
+ if (CurrentSong.Video <> '') and FileExists(CurrentSong.Path + CurrentSong.Video) then
+ begin
+ if (fCurrentVideoPlaybackEngine.Open(CurrentSong.Path + CurrentSong.Video)) then
+ begin
+ fShowVisualization := False;
+ fCurrentVideoPlaybackEngine := VideoPlayback;
+ fCurrentVideoPlaybackEngine.Position := CurrentSong.VideoGAP + CurrentSong.Start;
+ CurrentSong.VideoLoaded := True;
+ fCurrentVideoPlaybackEngine.play;
+ end;
+ end;
+
+{**
+ * set background to: picture
+ *}
+ if (CurrentSong.Background <> '') and (CurrentSong.VideoLoaded = False)
+ and (TVisualizerOption(Ini.VisualizerOption) = voOff) then
+ try
+ Tex_Background := Texture.LoadTexture(CurrentSong.Path + CurrentSong.Background);
+ except
+ Log.LogError('Background could not be loaded: ' + CurrentSong.Path +
+ CurrentSong.Background);
+ Tex_Background.TexNum := 0;
+ end
+ else
+ Tex_Background.TexNum := 0;
+{**
+ * set background to: visualization (Overwrites all)
+ *}
+ if (TVisualizerOption(Ini.VisualizerOption) in [voOn]) then
+ begin
+ fShowVisualization := True;
+ fCurrentVideoPlaybackEngine := Visualization;
+ fCurrentVideoPlaybackEngine.play;
+ end;
+
+{**
+ * set background to: visualization (Videos are still shown)
+ *}
+ if ((TVisualizerOption(Ini.VisualizerOption) in [voWhenNoVideo]) and
+ (CurrentSong.VideoLoaded = False)) then
+ begin
+ fShowVisualization := True;
+ fCurrentVideoPlaybackEngine := Visualization;
+ fCurrentVideoPlaybackEngine.play;
+ end;
+
+ // prepare lyrics timer
+ LyricsState.Reset();
+ LyricsState.SetCurrentTime(CurrentSong.Start);
+ LyricsState.StartTime := CurrentSong.Gap;
+ if (CurrentSong.Finish > 0) then
+ LyricsState.TotalTime := CurrentSong.Finish / 1000
+ else
+ LyricsState.TotalTime := AudioPlayback.Length;
+ LyricsState.UpdateBeats();
+
+ // prepare music
+ AudioPlayback.Stop();
+ AudioPlayback.Position := CurrentSong.Start;
+ // synchronize music to the lyrics
+ AudioPlayback.SetSyncSource(LyricsSync);
+
+ // prepare and start voice-capture
+ AudioInput.CaptureStart;
+
+ for P := 0 to High(Player) do
+ ClearScores(P);
+
+ // main text
+ Lyrics.Clear(CurrentSong.BPM[0].BPM, CurrentSong.Resolution);
+
+ // set custom options
+ case Ini.LyricsFont of
+ 0:
+ begin
+ Lyrics.UpperLineSize := 14;
+ Lyrics.LowerLineSize := 14;
+ Lyrics.FontStyle := 0;
+
+ Lyrics.LineColor_en.R := Skin_FontR;
+ Lyrics.LineColor_en.G := Skin_FontG;
+ Lyrics.LineColor_en.B := Skin_FontB;
+ Lyrics.LineColor_en.A := 1;
+
+ Lyrics.LineColor_dis.R := 0.4;
+ Lyrics.LineColor_dis.G := 0.4;
+ Lyrics.LineColor_dis.B := 0.4;
+ Lyrics.LineColor_dis.A := 1;
+
+ Lyrics.LineColor_act.R := 5 / 256;
+ Lyrics.LineColor_act.G := 163 / 256;
+ Lyrics.LineColor_act.B := 210 / 256;
+ Lyrics.LineColor_act.A := 1;
+ end;
+ 1:
+ begin
+ Lyrics.UpperLineSize := 14;
+ Lyrics.LowerLineSize := 14;
+ Lyrics.FontStyle := 2;
+
+ Lyrics.LineColor_en.R := 0.75;
+ Lyrics.LineColor_en.G := 0.75;
+ Lyrics.LineColor_en.B := 1;
+ Lyrics.LineColor_en.A := 1;
+
+ Lyrics.LineColor_dis.R := 0.8;
+ Lyrics.LineColor_dis.G := 0.8;
+ Lyrics.LineColor_dis.B := 0.8;
+ Lyrics.LineColor_dis.A := 1;
+
+ Lyrics.LineColor_act.R := 0.5;
+ Lyrics.LineColor_act.G := 0.5;
+ Lyrics.LineColor_act.B := 1;
+ Lyrics.LineColor_act.A := 1;
+ end;
+ 2:
+ begin
+ Lyrics.UpperLineSize := 12;
+ Lyrics.LowerLineSize := 12;
+ Lyrics.FontStyle := 3;
+
+ Lyrics.LineColor_en.R := 0.75;
+ Lyrics.LineColor_en.G := 0.75;
+ Lyrics.LineColor_en.B := 1;
+ Lyrics.LineColor_en.A := 1;
+
+ Lyrics.LineColor_dis.R := 0.8;
+ Lyrics.LineColor_dis.G := 0.8;
+ Lyrics.LineColor_dis.B := 0.8;
+ Lyrics.LineColor_dis.A := 1;
+
+ Lyrics.LineColor_act.R := 0.5;
+ Lyrics.LineColor_act.G := 0.5;
+ Lyrics.LineColor_act.B := 1;
+ Lyrics.LineColor_act.A := 1;
+ end;
+ end; // case
+
+ // Initialize lyrics by filling its queue
+ while (not Lyrics.IsQueueFull) and (Lyrics.LineCounter <=
+ High(Lines[0].Line)) do
+ begin
+ Lyrics.AddLine(@Lines[0].Line[Lyrics.LineCounter]);
+ end;
+
+ // Deactivate pause
+ Paused := False;
+
+ // Kill all stars not killed yet (GoldenStarsTwinkle Mod)
+ GoldenRec.SentenceChange;
+
+ // set Position of Line Bonus - Line Bonus end
+ // set number of empty sentences for Line Bonus
+ NumEmptySentences := 0;
+ for P := Low(Lines[0].Line) to High(Lines[0].Line) do
+ if Lines[0].Line[P].TotalNotes = 0 then
+ Inc(NumEmptySentences);
+
+ Log.LogStatus('End', 'onShow');
+end;
+
+procedure TScreenSing.onShowFinish;
+begin
+ // start lyrics
+ LyricsState.Resume();
+
+ // start music
+ AudioPlayback.Play();
+
+ // start timer
+ CountSkipTimeSet;
+end;
+
+function TScreenSing.Draw: boolean;
+var
+ Min: integer;
+ Sec: integer;
+ Tekst: string;
+ Flash: real;
+ S: integer;
+ T: integer;
+ CurLyricsTime: real;
+begin
+
+ // set player names (for 2 screens and only Singstar skin)
+ if ScreenAct = 1 then
+ begin
+ Text[TextP1].Text := 'P1';
+ Text[TextP1TwoP].Text := 'P1';
+ Text[TextP1ThreeP].Text := 'P1';
+ Text[TextP2R].Text := 'P2';
+ Text[TextP2M].Text := 'P2';
+ Text[TextP3R].Text := 'P3';
+ end;
+
+ if ScreenAct = 2 then
+ begin
+ case PlayersPlay of
+ 4:
+ begin
+ Text[TextP1TwoP].Text := 'P3';
+ Text[TextP2R].Text := 'P4';
+ end;
+ 6:
+ begin
+ Text[TextP1ThreeP].Text := 'P4';
+ Text[TextP2M].Text := 'P5';
+ Text[TextP3R].Text := 'P6';
+ end;
+ end; // case
+ end; // if
+
+
+ ////
+ // dual screen, part 1
+ ////////////////////////
+
+ // Note: ScreenX is the offset of the current screen in dual-screen mode so we
+ // will move the statics and texts to the correct screen here.
+ // FIXME: clean up this weird stuff. Commenting this stuff out, nothing
+ // was missing on screen w/ 6 players - so do we even need this stuff?
+ Static[StaticP1].Texture.X := Static[StaticP1].Texture.X + 10 * ScreenX;
+
+ Text[TextP1].X := Text[TextP1].X + 10 * ScreenX;
+
+ {Static[StaticP1ScoreBG].Texture.X := Static[StaticP1ScoreBG].Texture.X + 10*ScreenX;
+ Text[TextP1Score].X := Text[TextP1Score].X + 10*ScreenX;}
+
+
+ Static[StaticP2R].Texture.X := Static[StaticP2R].Texture.X + 10 * ScreenX;
+
+ Text[TextP2R].X := Text[TextP2R].X + 10 * ScreenX;
+
+ {Static[StaticP2RScoreBG].Texture.X := Static[StaticP2RScoreBG].Texture.X + 10*ScreenX;
+ Text[TextP2RScore].X := Text[TextP2RScore].X + 10*ScreenX;}
+
+ // end of weird stuff
+
+ Static[1].Texture.X := Static[1].Texture.X + 10 * ScreenX;
+
+ for T := 0 to 1 do
+ Text[T].X := Text[T].X + 10 * ScreenX;
+
+
+
+ // retrieve current lyrics time, we have to store the value to avoid
+ // that min- and sec-values do not match
+ CurLyricsTime := LyricsState.GetCurrentTime();
+ Min := Round(CurLyricsTime) div 60;
+ Sec := Round(CurLyricsTime) mod 60;
+
+ // update static menu with time ...
+ Text[TextTimeText].Text := '';
+ if Min < 10 then
+ Text[TextTimeText].Text := '0';
+ Text[TextTimeText].Text := Text[TextTimeText].Text + IntToStr(Min) + ':';
+ if Sec < 10 then
+ Text[TextTimeText].Text := Text[TextTimeText].Text + '0';
+ Text[TextTimeText].Text := Text[TextTimeText].Text + IntToStr(Sec);
+
+ // draw static menu (BG)
+ // Note: there is no menu and the animated background brakes the video playback
+ //DrawBG;
+
+ // Draw Background
+ SingDrawBackground;
+
+ // update and draw movie
+ if (ShowFinish and (CurrentSong.VideoLoaded or fShowVisualization)) then
+ begin
+ if assigned(fCurrentVideoPlaybackEngine) then
+ begin
+ fCurrentVideoPlaybackEngine.GetFrame(LyricsState.GetCurrentTime());
+ fCurrentVideoPlaybackEngine.DrawGL(ScreenAct);
+ end;
+ end;
+
+ // draw static menu (FG)
+ DrawFG;
+
+ // check for music finish
+ //Log.LogError('Check for music finish: ' + BoolToStr(Music.Finished) + ' ' + FloatToStr(LyricsState.CurrentTime*1000) + ' ' + IntToStr(CurrentSong.Finish));
+ if ShowFinish then
+ begin
+ if (not AudioPlayback.Finished) and ((CurrentSong.Finish = 0) or
+ (LyricsState.GetCurrentTime() * 1000 <= CurrentSong.Finish)) then
+ begin
+ // analyze song if not paused
+ if (not Paused) then
+ Sing(Self);
+ end
+ else
+ begin
+ if (not FadeOut) then
+ begin
+ Finish;
+ FadeOut := True;
+ FadeTo(@ScreenScore);
+ end;
+ end;
+ end;
+
+ // always draw custom items
+ SingDraw;
+
+ //GoldenNoteStarsTwinkle
+ GoldenRec.SpawnRec;
+
+ //Draw Scores
+ Scores.Draw;
+
+ ////
+ // dual screen, part 2
+ ////////////////////////
+
+ // Note: ScreenX is the offset of the current screen in dual-screen mode so we
+ // will move the statics and texts to the correct screen here.
+ // FIXME: clean up this weird stuff
+
+ Static[StaticP1].Texture.X := Static[StaticP1].Texture.X - 10 * ScreenX;
+ Text[TextP1].X := Text[TextP1].X - 10 * ScreenX;
+
+ Static[StaticP2R].Texture.X := Static[StaticP2R].Texture.X - 10 * ScreenX;
+ Text[TextP2R].X := Text[TextP2R].X - 10 * ScreenX;
+
+ //end of weird
+
+ Static[1].Texture.X := Static[1].Texture.X - 10 * ScreenX;
+
+ for T := 0 to 1 do
+ Text[T].X := Text[T].X - 10 * ScreenX;
+
+ // Draw Pausepopup
+ // FIXME: this is a workaround that the Static is drawn over the Lyrics, Lines, Scores and Effects
+ // maybe someone could find a better solution
+ if Paused then
+ begin
+ Static[StaticPausePopup].Visible := True;
+ Static[StaticPausePopup].Draw;
+ Static[StaticPausePopup].Visible := False;
+ end;
+
+ Result := True;
+end;
+
+procedure TScreenSing.Finish;
+begin
+ AudioInput.CaptureStop;
+ AudioPlayback.Stop;
+ AudioPlayback.SetSyncSource(nil);
+
+ if (Ini.SavePlayback = 1) then
+ begin
+ Log.BenchmarkStart(0);
+ Log.LogVoice(0);
+ Log.LogVoice(1);
+ Log.LogVoice(2);
+ Log.BenchmarkEnd(0);
+ Log.LogBenchmark('Creating files', 0);
+ end;
+
+ if CurrentSong.VideoLoaded then
+ begin
+ fCurrentVideoPlaybackEngine.Close;
+ CurrentSong.VideoLoaded := False; // to prevent drawing closed video
+ end;
+
+ SetFontItalic(False);
+end;
+
+procedure TScreenSing.OnSentenceEnd(SentenceIndex: cardinal);
+var
+ PlayerIndex: integer;
+ CurrentPlayer: PPLayer;
+ CurrentScore: real;
+ Line: PLine;
+ LinePerfection: real; // perfection of singing performance on the current line
+ Rating: integer;
+ LineScore: real;
+ LineBonus: real;
+ MaxSongScore: integer; // max. points for the song (without line bonus)
+ MaxLineScore: real; // max. points for the current line
+const
+ // TODO: move this to a better place
+ MAX_LINE_RATING = 8; // max. rating for singing performance
+begin
+ Line := @Lines[0].Line[SentenceIndex];
+
+ // check for empty sentence
+ if (Line.TotalNotes <= 0) then
+ Exit;
+
+ // set max song score
+ if (Ini.LineBonus = 0) then
+ MaxSongScore := MAX_SONG_SCORE
+ else
+ MaxSongScore := MAX_SONG_SCORE - MAX_SONG_LINE_BONUS;
+
+ // Note: ScoreValue is the sum of all note values of the song
+ MaxLineScore := MaxSongScore * (Line.TotalNotes / Lines[0].ScoreValue);
+
+ for PlayerIndex := 0 to High(Player) do
+ begin
+ CurrentPlayer := @Player[PlayerIndex];
+ CurrentScore := CurrentPlayer.Score + CurrentPlayer.ScoreGolden;
+
+ // Line Bonus
+
+ // points for this line
+ LineScore := CurrentScore - CurrentPlayer.ScoreLast;
+
+ // determine LinePerfection
+ // Note: the "+2" extra points are a little bonus so the player does not
+ // have to be that perfect to reach the bonus steps.
+ LinePerfection := (LineScore + 2) / MaxLineScore;
+
+ // clamp LinePerfection to range [0..1]
+ if (LinePerfection < 0) then
+ LinePerfection := 0
+ else if (LinePerfection > 1) then
+ LinePerfection := 1;
+
+ // add line-bonus if enabled
+ if (Ini.LineBonus > 0) then
+ begin
+ // line-bonus points (same for each line, no matter how long the line is)
+ LineBonus := MAX_SONG_LINE_BONUS / (Length(Lines[0].Line) -
+ NumEmptySentences);
+ // apply line-bonus
+ CurrentPlayer.ScoreLine :=
+ CurrentPlayer.ScoreLine + LineBonus * LinePerfection;
+ CurrentPlayer.ScoreLineInt := Round(CurrentPlayer.ScoreLine / 10) * 10;
+ // update total score
+ CurrentPlayer.ScoreTotalInt :=
+ CurrentPlayer.ScoreInt +
+ CurrentPlayer.ScoreGoldenInt
+ + CurrentPlayer.ScoreLineInt;
+
+ // spawn rating pop-up
+ Rating := Round(LinePerfection * MAX_LINE_RATING);
+ Scores.SpawnPopUp(PlayerIndex, Rating, CurrentPlayer.ScoreTotalInt);
+ end;
+
+ // PerfectLineTwinkle (effect), Part 1
+ if (Ini.EffectSing = 1) then
+ CurrentPlayer.LastSentencePerfect := (LinePerfection >= 1);
+
+ // refresh last score
+ CurrentPlayer.ScoreLast := CurrentScore;
+ end;
+
+ // PerfectLineTwinkle (effect), Part 2
+ if (Ini.EffectSing = 1) then
+ GoldenRec.SpawnPerfectLineTwinkle;
+end;
+
+ // Called on sentence change
+ // SentenceIndex: index of the new active sentence
+procedure TScreenSing.OnSentenceChange(SentenceIndex: cardinal);
+var
+ LyricEngine: TLyricEngine;
+begin
+ //GoldenStarsTwinkle
+ GoldenRec.SentenceChange;
+
+ // Fill lyrics queue and set upper line to the current sentence
+ while (Lyrics.GetUpperLineIndex() < SentenceIndex) or
+ (not Lyrics.IsQueueFull) do
+ begin
+ // Add the next line to the queue or a dummy if no more lines are available
+ if (Lyrics.LineCounter <= High(Lines[0].Line)) then
+ Lyrics.AddLine(@Lines[0].Line[Lyrics.LineCounter])
+ else
+ Lyrics.AddLine(nil);
+ end;
+
+ // AddLine draws the passed line to the back-buffer of the render context
+ // and copies it into a texture afterwards (offscreen rendering).
+ // This leaves an in invalidated screen. Calling Draw() makes sure,
+ // that the back-buffer stores the sing-screen, when the next
+ // swap between the back- and front-buffer is done (eliminates flickering)
+ // <note> calling AddLine() right before the regular screen update (Display.Draw)
+ // would be a better solution.</note>
+ Draw;
+end;
+
+function TLyricsSyncSource.GetClock(): real;
+begin
+ Result := LyricsState.GetCurrentTime();
+end;
+
+end.
+