aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/base/UDraw.pas18
-rw-r--r--src/base/UNote.pas44
-rw-r--r--src/base/USong.pas330
-rw-r--r--src/screens/UScreenScore.pas209
-rw-r--r--src/screens/UScreenSing.pas781
-rw-r--r--src/screens/UScreenSong.pas207
6 files changed, 1295 insertions, 294 deletions
diff --git a/src/base/UDraw.pas b/src/base/UDraw.pas
index 5bec3eab..374f9cbe 100644
--- a/src/base/UDraw.pas
+++ b/src/base/UDraw.pas
@@ -93,6 +93,7 @@ uses
UMusic,
URecord,
UScreenSing,
+ USong,
UTexture;
procedure SingDrawBackground;
@@ -1115,6 +1116,8 @@ var
width, height: real;
LyricsProgress: real;
CurLyricsTime: real;
+ TotalTime: real;
+
begin
x := Theme.Sing.StaticTimeProgress.x;
y := Theme.Sing.StaticTimeProgress.y;
@@ -1135,13 +1138,22 @@ begin
glTexCoord2f(0, 0);
glVertex2f(x, y);
- CurLyricsTime := LyricsState.GetCurrentTime();
+ if ScreenSong.Mode = smMedley then
+ begin
+ CurLyricsTime := LyricsState.GetCurrentTime() - ScreenSing.MedleyStart;
+ TotalTime := ScreenSing.MedleyEnd - ScreenSing.MedleyStart;
+ end
+ else
+ begin
+ CurLyricsTime := LyricsState.GetCurrentTime();
+ TotalTime := LyricsState.TotalTime;
+ end;
if (CurLyricsTime > 0) and
(LyricsState.TotalTime > 0) then
begin
- LyricsProgress := CurLyricsTime / LyricsState.TotalTime;
+ LyricsProgress := CurLyricsTime / TotalTime;
// avoid that the bar "overflows" for inaccurate song lengths
- if (LyricsProgress > 1.0) then
+ if LyricsProgress > 1.0 then
LyricsProgress := 1.0;
glTexCoord2f((width * LyricsProgress) / 8, 0);
glVertex2f(x + width * LyricsProgress, y);
diff --git a/src/base/UNote.pas b/src/base/UNote.pas
index 2d71fb67..b9e71ac2 100644
--- a/src/base/UNote.pas
+++ b/src/base/UNote.pas
@@ -37,16 +37,15 @@ uses
SysUtils,
Classes,
SDL,
- UMusic,
- URecord,
- UTime,
+ gl,
UDisplay,
UIni,
ULog,
ULyrics,
+ URecord,
UScreenSing,
USong,
- gl;
+ UTime;
type
PPLayerNote = ^TPlayerNote;
@@ -88,6 +87,21 @@ type
Note: array of TPlayerNote;
end;
+ TStats = record
+ Player: array of TPlayer;
+ SongArtist: string;
+ SongTitle: string;
+ end;
+
+ TMedleyPlaylist = record
+ Song: array of integer;
+ NumMedleySongs: integer;
+ CurrentMedleySong: integer;
+ ApplausePlayed: boolean;
+ Stats: array of TStats;
+ NumPlayer: integer;
+ end;
+
{* Player and music info *}
var
{**
@@ -107,6 +121,8 @@ var
*}
CurrentSong: TSong;
+ PlaylistMedley: TMedleyPlaylist; // playlist medley
+
const
MAX_SONG_SCORE = 10000; // max. achievable points per song
MAX_SONG_LINE_BONUS = 1000; // max. achievable line bonus per song
@@ -124,23 +140,23 @@ implementation
uses
Math,
StrUtils,
- USongs,
- UJoystick,
+ UCatCovers,
UCommandLine,
- ULanguage,
- //SDL_ttf,
- USkins,
+ UCommon,
+ UConfig,
UCovers,
- UCatCovers,
UDataBase,
- UPlaylist,
- UParty,
- UConfig,
- UCommon,
UGraphic,
UGraphicClasses,
+ UJoystick,
+ ULanguage,
+ UMusic,
+ UParty,
UPathUtils,
UPlatform,
+ UPlaylist,
+ USkins,
+ USongs,
UThemes;
function GetTimeForBeats(BPM, Beats: real): real;
diff --git a/src/base/USong.pas b/src/base/USong.pas
index e92c5b45..38ba1c12 100644
--- a/src/base/USong.pas
+++ b/src/base/USong.pas
@@ -45,10 +45,6 @@ uses
{$ENDIF}
SysUtils,
Classes,
- UPlatform,
- ULog,
- UTexture,
- UCommon,
{$IFDEF DARWIN}
cthreads,
{$ENDIF}
@@ -56,15 +52,29 @@ uses
PseudoThread,
{$ENDIF}
UCatCovers,
- UXMLSong,
- UUnicodeUtils,
- UTextEncoding,
+ UCommon,
UFilesystem,
- UPath;
+ ULog,
+ UPath,
+ UPlatform,
+ UTexture,
+ UTextEncoding,
+ UUnicodeUtils,
+ UXMLSong;
type
- TSingMode = ( smNormal, smPartyMode, smPlaylistRandom );
+ TSingMode = ( smNormal, smPartyMode, smPlaylistRandom, smMedley );
+
+ TMedleySource = ( msNone, msCalculated, msTag );
+
+ TMedley = record
+ Source: TMedleySource; //source of the information
+ StartBeat: integer; //start beat of medley
+ EndBeat: integer; //end beat of medley
+ FadeIn_time: real; //FadeIn-Time in seconds
+ FadeOut_time: real; //FadeOut-Time in seconds
+ end;
TBPM = record
BPM: real;
@@ -92,6 +102,7 @@ type
function DecodeFilename(Filename: RawByteString): IPath;
procedure ParseNote(LineNumber: integer; TypeP: char; StartP, DurationP, NoteP: integer; LyricS: UTF8String);
procedure NewSentence(LineNumberP: integer; Param1, Param2: integer);
+ procedure FindRefrain(); // tries to find a refrain for the medley mode and preview start
function ParseLyricStringParam(const Line: RawByteString; var LinePos: integer): RawByteString;
function ParseLyricIntParam(const Line: RawByteString; var LinePos: integer): integer;
@@ -139,6 +150,10 @@ type
Encoding: TEncoding;
+ PreviewStart: real; // in seconds
+ CalcMedley: boolean; // if true => do not calc medley for that song
+ Medley: TMedley; // medley params
+
CustomTags: array of TCustomHeaderTag;
Score: array[0..2] of array of TScore;
@@ -166,6 +181,7 @@ type
function LoadXMLSong: boolean;
function Analyse(const ReadCustomTags: Boolean = false): boolean;
function AnalyseXML(): boolean;
+ procedure SetMedleyMode();
procedure Clear();
end;
@@ -175,9 +191,13 @@ uses
StrUtils,
TextGL,
UIni,
- UPathUtils,
UMusic, //needed for Lines
- UNote; //needed for Player
+ UNote, //needed for Player
+ UPathUtils;
+
+const
+ DEFAULT_FADE_IN_TIME = 8; // for medley fade-in
+ DEFAULT_FADE_OUT_TIME = 2; // for medley fade-out
constructor TSong.Create();
begin
@@ -468,6 +488,7 @@ begin
Exit;
end;
+ SetLength(Lines, 0);
SetLength(Lines, 2);
for Count := 0 to High(Lines) do
begin
@@ -867,6 +888,7 @@ var
Value: string;
SepPos: integer; // separator position
Done: byte; // bit-vector of mandatory fields
+ MedleyFlags: byte; //bit-vector for medley/preview tags
EncFile: IPath; // encoded filename
FullFileName: string;
@@ -887,6 +909,7 @@ var
begin
Result := true;
Done := 0;
+ MedleyFlags := 0;
FullFileName := Path.Append(Filename).ToNative;
@@ -1090,6 +1113,35 @@ begin
self.Encoding := ParseEncoding(Value, Ini.DefaultEncoding);
end
+ // PreviewStart
+ else if (Identifier = 'PREVIEWSTART') then
+ begin
+ self.PreviewStart := StrToFloatI18n( Value );
+ if (self.PreviewStart>0) then
+ MedleyFlags := MedleyFlags or 1;
+ end
+
+ // MedleyStartBeat
+ else if (Identifier = 'MEDLEYSTARTBEAT') and not self.Relative then
+ begin
+ if TryStrtoInt(Value, self.Medley.StartBeat) then
+ MedleyFlags := MedleyFlags or 2;
+ end
+
+ // MedleyEndBeat
+ else if (Identifier = 'MEDLEYENDBEAT') and not self.Relative then
+ begin
+ if TryStrtoInt(Value, self.Medley.EndBeat) then
+ MedleyFlags := MedleyFlags or 4;
+ end
+
+ // Medley
+ else if (Identifier = 'CALCMEDLEY') then
+ begin
+ if Uppercase(Value) = 'OFF' then
+ self.CalcMedley := false;
+ end
+
// unsupported tag
else
begin
@@ -1099,7 +1151,7 @@ begin
end; // End check for non-empty Value
// read next line
- if (not SongFile.ReadLine(Line)) then
+ if not SongFile.ReadLine(Line) then
begin
Result := false;
Log.LogError('File incomplete or not Ultrastar txt (A): ' + FullFileName);
@@ -1124,6 +1176,33 @@ begin
Log.LogError('Title tag missing: ' + FullFileName)
else //unknown Error
Log.LogError('File incomplete or not Ultrastar txt (B - '+ inttostr(Done) +'): ' + FullFileName);
+ end
+ else
+ begin //check medley tags
+ if (MedleyFlags and 6) = 6 then //MedleyStartBeat and MedleyEndBeat are both set
+ begin
+ if self.Medley.StartBeat >= self.Medley.EndBeat then
+ MedleyFlags := MedleyFlags - 6;
+ end;
+
+ if ((MedleyFlags and 1) = 0) or (self.PreviewStart <= 0) then //PreviewStart is not set or <=0
+ begin
+ if (MedleyFlags and 2) = 2 then
+ self.PreviewStart := GetTimeFromBeat(self.Medley.StartBeat) //fallback to MedleyStart
+ else
+ self.PreviewStart := 0; //else set it to 0, it will be set in FindRefrainStart
+ end;
+
+ if (MedleyFlags and 6) = 6 then
+ begin
+ self.Medley.Source := msTag;
+
+ //calculate fade time
+ self.Medley.FadeIn_time := DEFAULT_FADE_IN_TIME;
+
+ self.Medley.FadeOut_time := DEFAULT_FADE_OUT_TIME;
+ end else
+ self.Medley.Source := msNone;
end;
end;
@@ -1223,6 +1302,221 @@ begin
Lines[LineNumberP].Line[Lines[LineNumberP].High].LastLine := false;
end;
+{* new procedure for preview
+ tries find out the beginning of a refrain
+ and the end... *}
+procedure TSong.FindRefrain();
+Const
+ MEDLEY_MIN_DURATION = 40; //minimum duration of a medley-song in seconds
+
+Type
+ TSeries = record
+ start: integer; //Start sentence of series
+ end_: integer; //End sentence of series
+ len: integer; //Length of sentence series
+ end;
+
+var
+ I, J, K, num_lines: integer;
+ sentences: array of UTF8String;
+ series: array of TSeries;
+ temp_series: TSeries;
+ max: integer;
+ len_lines, len_notes: integer;
+ found_end: boolean;
+begin
+ if self.Medley.Source = msTag then
+ Exit;
+
+ if not self.CalcMedley then
+ Exit;
+
+ //relative is not supported for medley!
+ if self.Relative then
+ begin
+ Log.LogError('Song '+self.Artist+'-' + self.Title + ' contains #Relative, this is not supported by medley-function!');
+ self.Medley.Source := msNone;
+ Exit;
+ end;
+
+ num_lines := Length(Lines[0].Line);
+ SetLength(sentences, num_lines);
+
+ //build sentences array
+ for I := 0 to num_lines - 1 do
+ begin
+ sentences[I] := '';
+ for J := 0 to Length(Lines[0].Line[I].Note) - 1 do
+ begin
+ if (Lines[0].Line[I].Note[J].NoteType <> ntFreestyle) then
+ sentences[I] := sentences[I] + Lines[0].Line[I].Note[J].Text;
+ end;
+ end;
+
+ //find equal sentences series
+ SetLength(series, 0);
+
+ for I := 0 to num_lines - 2 do
+ begin
+ for J := I + 1 to num_lines - 1 do
+ begin
+ if sentences[I] = sentences[J] then
+ begin
+ temp_series.start := I;
+ temp_series.end_ := I;
+
+ if (J + J - I - 1 > num_lines - 1) then
+ max:=num_lines-1-J
+ else
+ max:=J-I-1;
+
+ for K := 1 to max do
+ begin
+ if sentences[I+K] = sentences[J+K] then
+ temp_series.end_ := I+K
+ else
+ break;
+ end;
+ temp_series.len := temp_series.end_ - temp_series.start + 1;
+ SetLength(series, Length(series)+1);
+ series[Length(series)-1] := temp_series;
+ end;
+ end;
+ end;
+
+ //search for longest sequence
+ max := 0;
+ if Length(series) > 0 then
+ begin
+ for I := 0 to Length(series) - 1 do
+ begin
+ if series[I].len > series[max].len then
+ max := I;
+ end;
+ end;
+
+ len_lines := length(Lines[0].Line);
+
+ if (Length(series) > 0) and (series[max].len > 3) then
+ begin
+ self.Medley.StartBeat := Lines[0].Line[series[max].start].Note[0].Start;
+ len_notes := length(Lines[0].Line[series[max].end_].Note);
+ self.Medley.EndBeat := Lines[0].Line[series[max].end_].Note[len_notes - 1].Start +
+ Lines[0].Line[series[max].end_].Note[len_notes - 1].Length;
+
+ found_end := false;
+
+ //set end if duration > MEDLEY_MIN_DURATION
+ if GetTimeFromBeat(self.Medley.StartBeat) + MEDLEY_MIN_DURATION >
+ GetTimeFromBeat(self.Medley.EndBeat) then
+ begin
+ found_end := true;
+ end;
+
+ //estimate the end: just go MEDLEY_MIN_DURATION
+ //ahead an set to a line end (if possible)
+ if not found_end then
+ begin
+ for I := series[max].start + 1 to len_lines - 1 do
+ begin
+ len_notes := length(Lines[0].Line[I].Note);
+ for J := 0 to len_notes - 1 do
+ begin
+ if GetTimeFromBeat(self.Medley.StartBeat) + MEDLEY_MIN_DURATION >
+ GetTimeFromBeat(Lines[0].Line[I].Note[J].Start +
+ Lines[0].Line[I].Note[J].Length) then
+ begin
+ found_end := true;
+ self.Medley.EndBeat := Lines[0].Line[I].Note[len_notes-1].Start +
+ Lines[0].Line[I].Note[len_notes - 1].Length;
+ break;
+ end;
+ end;
+ end;
+ end;
+
+ if found_end then
+ begin
+ self.Medley.Source := msCalculated;
+
+ //calculate fade time
+ self.Medley.FadeIn_time := DEFAULT_FADE_IN_TIME;
+ self.Medley.FadeOut_time := DEFAULT_FADE_OUT_TIME;
+ end;
+ end;
+
+ //set PreviewStart if not set
+ if self.PreviewStart = 0 then
+ begin
+ if self.Medley.Source = msCalculated then
+ self.PreviewStart := GetTimeFromBeat(self.Medley.StartBeat);
+ end;
+end;
+
+//sets a song to medley-mode:
+//converts all unneeded notes into freestyle
+//updates score values
+procedure TSong.SetMedleyMode();
+var
+ pl, line, note: integer;
+ cut_line: array of integer;
+ foundcut: array of boolean;
+ start: integer;
+ end_: integer;
+
+begin
+ start := self.Medley.StartBeat;
+ end_ := self.Medley.EndBeat;
+ SetLength(cut_line, Length(Lines));
+ SetLength(foundcut, Length(Lines));
+
+ for pl := 0 to Length(Lines) - 1 do
+ begin
+ foundcut[pl] := false;
+ cut_line[pl] := high(integer);
+ Lines[pl].ScoreValue := 0;
+ for line := 0 to Length(Lines[pl].Line) - 1 do
+ begin
+ Lines[pl].Line[line].TotalNotes := 0;
+ for note := 0 to Length(Lines[pl].Line[line].Note) - 1 do
+ begin
+ if Lines[pl].Line[line].Note[note].Start < start then //check start
+ begin
+ Lines[pl].Line[line].Note[note].NoteType := ntFreeStyle;
+ end else if Lines[pl].Line[line].Note[note].Start>= end_ then //check end
+ begin
+ Lines[pl].Line[line].Note[note].NoteType := ntFreeStyle;
+ if not foundcut[pl] then
+ begin
+ if (note=0) then
+ cut_line[pl] := line
+ else
+ cut_line[pl] := line + 1;
+ end;
+ foundcut[pl] := true;
+ end
+ else
+ begin
+ //add this notes value ("notes length" * "notes scorefactor") to the current songs entire value
+ Inc(Lines[pl].ScoreValue, Lines[pl].Line[line].Note[note].Length * ScoreFactor[Lines[pl].Line[line].Note[note].NoteType]);
+ //and to the current lines entire value
+ Inc(Lines[pl].Line[line].TotalNotes, Lines[pl].Line[line].Note[note].Length * ScoreFactor[Lines[pl].Line[line].Note[note].NoteType]);
+ end;
+ end;
+ end;
+ end;
+
+ for pl := 0 to Length(Lines) - 1 do
+ begin
+ if (foundcut[pl]) and (Length(Lines[pl].Line) > cut_line[pl]) then
+ begin
+ SetLength(Lines[pl].Line, cut_line[pl]);
+ Lines[pl].High := cut_line[pl]-1;
+ Lines[pl].Number := Lines[pl].High+1;
+ end;
+ end;
+end;
+
procedure TSong.Clear();
begin
//Main Information
@@ -1257,6 +1551,9 @@ begin
NotesGAP := 0;
Resolution := 4;
Creator := '';
+ PreviewStart := 0;
+ CalcMedley := true;
+ Medley.Source := msNone;
Relative := false;
end;
@@ -1277,7 +1574,14 @@ begin
Self.clear;
//Read Header
- Result := Self.ReadTxTHeader(SongFile, ReadCustomTags)
+ Result := Self.ReadTxTHeader(SongFile, ReadCustomTags);
+
+ //Load Song for Medley Tags
+ CurrentSong := self;
+ Result := Result and LoadSong();
+
+ if Result then
+ Self.FindRefrain();
finally
SongFile.Free;
end;
diff --git a/src/screens/UScreenScore.pas b/src/screens/UScreenScore.pas
index de7675bf..4b7332b4 100644
--- a/src/screens/UScreenScore.pas
+++ b/src/screens/UScreenScore.pas
@@ -34,16 +34,16 @@ interface
{$I switches.inc}
uses
- UMenu,
- SDL,
SysUtils,
+ math,
+ SDL,
+ gl,
UDisplay,
+ UMenu,
UMusic,
USongs,
- UThemes,
- gl,
- math,
- UTexture;
+ UTexture,
+ UThemes;
const
ZBars: real = 0.8; // Z value for the bars
@@ -167,6 +167,11 @@ type
TextPhrase_ActualValue: array[1..6] of integer;
TextGolden_ActualValue: array[1..6] of integer;
+ ActualRound: integer;
+
+ procedure RefreshTexts;
+ procedure ResetScores;
+
procedure MapPlayersToPosition;
procedure FillPlayer(Item, P: integer);
@@ -196,7 +201,7 @@ type
public
constructor Create; override;
function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override;
- function ParseMouse(MouseButton: Integer; BtnDown: Boolean; X, Y: integer): boolean; override;
+ function ParseMouse(MouseButton: integer; BtnDown: boolean; X, Y: integer): boolean; override;
procedure OnShow; override;
procedure OnShowFinish; override;
function Draw: boolean; override;
@@ -206,14 +211,15 @@ implementation
uses
UGraphic,
- UScreenSong,
- UMenuStatic,
- UTime,
UIni,
- USkins,
ULog,
ULanguage,
+ UMenuStatic,
UNote,
+ UScreenSong,
+ USkins,
+ USong,
+ UTime,
UUnicodeUtils;
@@ -237,27 +243,96 @@ begin
SDLK_BACKSPACE,
SDLK_RETURN:
begin
- FadeTo(@ScreenTop5);
- Exit;
+ if ScreenSong.Mode = smMedley then
+ FadeTo(@ScreenSong)
+ else
+ FadeTo(@ScreenTop5);
+
+ Exit;
end;
SDLK_SYSREQ:
begin
Display.SaveScreenShot;
end;
+
+ SDLK_RIGHT:
+ begin
+ if ActualRound < Length(PlaylistMedley.Stats) - 1 then
+ begin
+ AudioPlayback.PlaySound(SoundLib.Change);
+ inc(ActualRound);
+ RefreshTexts;
+ end;
+ end;
+
+ SDLK_LEFT:
+ begin
+ if ActualRound > 0 then
+ begin
+ AudioPlayback.PlaySound(SoundLib.Change);
+ dec(ActualRound);
+ RefreshTexts;
+ end;
+ end;
end;
end;
end;
-function TScreenScore.ParseMouse(MouseButton: Integer; BtnDown: Boolean; X, Y: integer): boolean;
+function TScreenScore.ParseMouse(MouseButton: integer; BtnDown: boolean; X, Y: integer): boolean;
begin
Result := True;
- if (MouseButton = SDL_BUTTON_LEFT) and BtnDown then begin
+ if (MouseButton = SDL_BUTTON_LEFT) and BtnDown then
+ begin
//left-click anywhere sends return
ParseInput(SDLK_RETURN, 0, true);
end;
end;
+procedure TScreenScore.RefreshTexts;
+var
+ P: integer;
+
+begin
+ if ActualRound < Length(PlaylistMedley.Stats) - 1 then
+ begin
+ Text[TextArtist].Text := IntToStr(ActualRound+1) + '/' +
+ IntToStr(Length(PlaylistMedley.Stats)-1) + ': ' +
+ PlaylistMedley.Stats[ActualRound].SongArtist;
+ Text[TextTitle].Text := PlaylistMedley.Stats[ActualRound].SongTitle;
+ Text[TextTitle].Visible := true;
+ Text[TextArtistTitle].Text := IntToStr(ActualRound+1) + '/' +
+ IntToStr(Length(PlaylistMedley.Stats)-1) + ': ' +
+ PlaylistMedley.Stats[ActualRound].SongArtist +
+ ' - ' + PlaylistMedley.Stats[ActualRound].SongTitle;
+ end
+ else
+ begin
+ if ScreenSong.Mode = smMedley then
+ begin
+ Text[TextArtist].Text := Language.Translate('SING_TOTAL');
+ Text[TextTitle].Visible := false;
+ Text[TextArtistTitle].Text := Language.Translate('SING_TOTAL');
+ end
+ else
+ begin
+ Text[TextArtist].Text := PlaylistMedley.Stats[ActualRound].SongArtist;
+ Text[TextTitle].Text := PlaylistMedley.Stats[ActualRound].SongTitle;
+ Text[TextTitle].Visible := true;
+ Text[TextArtistTitle].Text := PlaylistMedley.Stats[ActualRound].SongArtist + ' - ' +
+ PlaylistMedley.Stats[ActualRound].SongTitle;
+ end;
+ end;
+
+ if ScreenSong.Mode = smMedley then
+ begin
+ for P := 0 to PlayersPlay - 1 do
+ Player[P] := PlaylistMedley.Stats[ActualRound].Player[P];
+ end;
+
+ ResetScores;
+end;
+
procedure TScreenScore.LoadSwapTextures;
var
P, I: integer;
@@ -621,23 +696,15 @@ begin
inherited;
- MapPlayersToPosition;
-
- for P := 1 to PlayersPlay do
+ ActualRound := 0;
+ if ScreenSong.Mode = smMedley then
begin
- // data
- aPlayerScoreScreenDatas[P].Bar_Y := Theme.Score.StaticBackLevel[PlayerPositionMap[P-1].Position].Y;
-
- // ratings
- aPlayerScoreScreenRatings[P].RateEaseStep := 1;
- aPlayerScoreScreenRatings[P].RateEaseValue := 20;
-
- // actual values
- TextScore_ActualValue[P] := 0;
- TextPhrase_ActualValue[P] := 0;
- TextGolden_ActualValue[P] := 0;
+ for P := 0 to PlayersPlay - 1 do
+ Player[P] := PlaylistMedley.Stats[ActualRound].Player[P];
end;
+ MapPlayersToPosition;
+
Text[TextArtist].Text := CurrentSong.Artist;
Text[TextTitle].Text := CurrentSong.Title;
Text[TextArtistTitle].Text := CurrentSong.Artist + ' - ' + CurrentSong.Title;
@@ -672,43 +739,29 @@ begin
for P := 1 to 6 do
begin
- Text[TextName[P]].Visible := V[P];
- Text[TextScore[P]].Visible := V[P];
-
- // We set alpha to 0 , so we can nicely blend them in when we need them
- Text[TextScore[P]].Alpha := 0;
- Text[TextNotesScore[P]].Alpha := 0;
- Text[TextNotes[P]].Alpha := 0;
- Text[TextLineBonus[P]].Alpha := 0;
- Text[TextLineBonusScore[P]].Alpha := 0;
- Text[TextGoldenNotes[P]].Alpha := 0;
- Text[TextGoldenNotesScore[P]].Alpha := 0;
- Text[TextTotal[P]].Alpha := 0;
- Text[TextTotalScore[P]].Alpha := 0;
- Statics[StaticBoxLightest[P]].Texture.Alpha := 0;
- Statics[StaticBoxLight[P]].Texture.Alpha := 0;
- Statics[StaticBoxDark[P]].Texture.Alpha := 0;
-
- Text[TextNotes[P]].Visible := V[P];
- Text[TextNotesScore[P]].Visible := V[P];
- Text[TextLineBonus[P]].Visible := V[P];
- Text[TextLineBonusScore[P]].Visible := V[P];
- Text[TextGoldenNotes[P]].Visible := V[P];
- Text[TextGoldenNotesScore[P]].Visible := V[P];
- Text[TextTotal[P]].Visible := V[P];
- Text[TextTotalScore[P]].Visible := V[P];
+ Text[TextName[P]].Visible := V[P];
+ Text[TextScore[P]].Visible := V[P];
+
+ Text[TextNotes[P]].Visible := V[P];
+ Text[TextNotesScore[P]].Visible := V[P];
+ Text[TextLineBonus[P]].Visible := V[P];
+ Text[TextLineBonusScore[P]].Visible := V[P];
+ Text[TextGoldenNotes[P]].Visible := V[P];
+ Text[TextGoldenNotesScore[P]].Visible := V[P];
+ Text[TextTotal[P]].Visible := V[P];
+ Text[TextTotalScore[P]].Visible := V[P];
for I := 0 to high(PlayerStatic[P]) do
Statics[PlayerStatic[P, I]].Visible := V[P];
for I := 0 to high(PlayerTexts[P]) do
- Text[PlayerTexts[P, I]].Visible := V[P];
+ Text[PlayerTexts[P, I]].Visible := V[P];
Statics[StaticBoxLightest[P]].Visible := V[P];
Statics[StaticBoxLight[P]].Visible := V[P];
Statics[StaticBoxDark[P]].Visible := V[P];
-
- Statics[StaticPlayerIdBox[P]].Visible := V[P];
+
+ Statics[StaticPlayerIdBox[P]].Visible := V[P];
// we draw that on our own
Statics[StaticBackLevel[P]].Visible := false;
@@ -717,9 +770,51 @@ begin
Statics[StaticLevelRound[P]].Visible := false;
end;
+ RefreshTexts;
+end;
+
+procedure TScreenScore.ResetScores;
+var
+ P: integer;
+
+begin
+ for P := 1 to PlayersPlay do
+ begin
+ // data
+ aPlayerScoreScreenDatas[P].Bar_Y := Theme.Score.StaticBackLevel[PlayerPositionMap[P-1].Position].Y;
+
+ // ratings
+ aPlayerScoreScreenRatings[P].RateEaseStep := 1;
+ aPlayerScoreScreenRatings[P].RateEaseValue := 20;
+
+ // actual values
+ TextScore_ActualValue[P] := 0;
+ TextPhrase_ActualValue[P] := 0;
+ TextGolden_ActualValue[P] := 0;
+ end;
+
+ for P := 1 to 6 do
+ begin
+ // We set alpha to 0 , so we can nicely blend them in when we need them
+ Text[TextScore[P]].Alpha := 0;
+ Text[TextNotesScore[P]].Alpha := 0;
+ Text[TextNotes[P]].Alpha := 0;
+ Text[TextLineBonus[P]].Alpha := 0;
+ Text[TextLineBonusScore[P]].Alpha := 0;
+ Text[TextGoldenNotes[P]].Alpha := 0;
+ Text[TextGoldenNotesScore[P]].Alpha := 0;
+ Text[TextTotal[P]].Alpha := 0;
+ Text[TextTotalScore[P]].Alpha := 0;
+ Statics[StaticBoxLightest[P]].Texture.Alpha := 0;
+ Statics[StaticBoxLight[P]].Texture.Alpha := 0;
+ Statics[StaticBoxDark[P]].Texture.Alpha := 0;
+ end;
+
BarScore_EaseOut_Step := 1;
BarPhrase_EaseOut_Step := 1;
BarGolden_EaseOut_Step := 1;
+
+ BarTime := SDL_GetTicks();
end;
procedure TScreenScore.onShowFinish;
diff --git a/src/screens/UScreenSing.pas b/src/screens/UScreenSing.pas
index 221526f8..ff4f33e6 100644
--- a/src/screens/UScreenSing.pas
+++ b/src/screens/UScreenSing.pas
@@ -36,24 +36,30 @@ interface
uses
SysUtils,
SDL,
- TextGL,
gl,
+ TextGL,
UFiles,
UGraphicClasses,
+ UHookableEvent,
UIni,
ULog,
ULyrics,
UMenu,
UMusic,
+ UPath,
USingScores,
USongs,
UTexture,
UThemes,
- UPath,
- UTime,
- UHookableEvent;
+ UTime;
type
+ TPos = record // Lines[part].Line[line].Note[note]
+ part: integer;
+ line: integer;
+ note: integer;
+ end;
+
TLyricsSyncSource = class(TSyncSource)
function GetClock(): real; override;
end;
@@ -72,11 +78,18 @@ type
TScreenSing = class(TMenu)
private
fShowVisualization: boolean;
- fCurrentVideo: IVideo;
- fVideoClip: IVideo;
- fLyricsSync: TLyricsSyncSource;
- fMusicSync: TMusicSyncSource;
- fTimebarMode: TTimebarMode;
+ fCurrentVideo: IVideo;
+ fVideoClip: IVideo;
+ fLyricsSync: TLyricsSyncSource;
+ fMusicSync: TMusicSyncSource;
+ fTimebarMode: TTimebarMode;
+
+ StartNote, EndNote: TPos;
+
+ procedure LoadNextSong();
+ procedure UpdateMedleyStats(medley_end: boolean);
+ procedure DrawMedleyCountdown();
+ procedure SongError();
protected
eSongLoaded: THookableEvent; //< event is called after lyrics of a song are loaded on OnShow
Paused: boolean; //pause Mod
@@ -97,6 +110,8 @@ type
StaticP1ThreeP: integer;
TextP1ThreeP: integer;
+ MedleyStart, MedleyEnd: real;
+
StaticP2R: integer;
TextP2R: integer;
@@ -120,12 +135,12 @@ type
// some settings to be set by plugins
Settings: record
- Finish: Boolean; //< if true, screen will finish on next draw
+ Finish: boolean; //< if true, screen will finish on next draw
- LyricsVisible: Boolean; //< shows or hides lyrics
- NotesVisible: Integer; //< if bit[playernum] is set the notes for the specified player are visible. By default all players notes are visible
+ LyricsVisible: boolean; //< shows or hides lyrics
+ NotesVisible: integer; //< if bit[playernum] is set the notes for the specified player are visible. By default all players notes are visible
- PlayerEnabled: Integer; //< defines whether a player can score atm
+ PlayerEnabled: integer; //< defines whether a player can score atm
end;
procedure ClearSettings;
procedure ApplySettings; //< applies changes of settings record
@@ -152,24 +167,24 @@ implementation
uses
Classes,
Math,
+ UDisplay,
UDraw,
UGraphic,
ULanguage,
UNote,
+ UParty,
URecord,
USong,
- UDisplay,
- UParty,
UUnicodeUtils;
// 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: UCS4Char;
+function TScreenSing.ParseInput(PressedKey: cardinal; CharCode: UCS4Char;
PressedDown: boolean): boolean;
begin
Result := true;
- if (PressedDown) then
+ if PressedDown then
begin // key down
// check normal keys
case UCS4UpperCase(CharCode) of
@@ -213,7 +228,7 @@ begin
// toggle time display
Ord('T'):
begin
- if (fTimebarMode = High(TTimebarMode)) then
+ if fTimebarMode = High(TTimebarMode) then
fTimebarMode := Low(TTimebarMode)
else
Inc(fTimebarMode);
@@ -228,6 +243,8 @@ begin
begin
// record sound hack:
//Sound[0].BufferLong
+ if ScreenSong.Mode = smMedley then
+ PlaylistMedley.NumMedleySongs := PlaylistMedley.CurrentMedleySong;
Finish;
FadeOut := true;
@@ -360,17 +377,14 @@ end;
procedure TScreenSing.OnShow;
var
- Index: integer;
V1: boolean;
V1TwoP: boolean; // position of score box in two player mode
V1ThreeP: boolean; // position of score box in three player mode
V2R: boolean;
V2M: boolean;
V3R: boolean;
- Color: TRGB;
- VideoFile, BgFile: IPath;
- success: boolean;
BadPlayer: integer;
+
begin
inherited;
@@ -382,22 +396,21 @@ begin
ClearSettings;
Party.CallBeforeSing;
- // reset video playback engine
- fCurrentVideo := nil;
-
- // 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?
+ // prepare players
+ SetLength(Player, PlayersPlay);
- // add new players
- for Index := 0 to PlayersPlay - 1 do
+ //Reset Player Medley stats
+ if (ScreenSong.Mode = smMedley) then
begin
- Scores.AddPlayer(Tex_ScoreBG[Index], Color);
- end;
+ PlaylistMedley.CurrentMedleySong:=1;
- Scores.Init; // get positions for players
+ PlaylistMedley.NumPlayer := PlayersPlay;
+ SetLength(PlaylistMedley.Stats, 0);
+
+ fTimebarMode := tbmRemaining;
+ end
+ else
+ fTimebarMode := tbmCurrent;
// prepare players
SetLength(Player, PlayersPlay);
@@ -472,11 +485,265 @@ begin
Statics[StaticP3R].Visible := V3R;
Text[TextP3R].Visible := V3R;
- fTimebarMode := tbmCurrent;
+ BadPlayer := AudioInputProcessor.CheckPlayersConfig(PlayersPlay);
+ if BadPlayer <> 0 then
+ begin
+ ScreenPopupError.ShowPopup(
+ Format(Language.Translate('ERROR_PLAYER_NO_DEVICE_ASSIGNMENT'),
+ [BadPlayer]));
+ end;
+
+ // set custom options
+ case Ini.LyricsFont of
+ 0: // normal fonts
+ begin
+ Lyrics.FontStyle := ftNormal;
+
+ 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 := 0.02;
+ Lyrics.LineColor_act.G := 0.6;
+ Lyrics.LineColor_act.B := 0.8;
+ Lyrics.LineColor_act.A := 1;
+ end;
+ 1, 2: // outline fonts
+ begin
+ if (Ini.LyricsFont = 1) then
+ Lyrics.FontStyle := ftOutline1
+ else
+ Lyrics.FontStyle := ftOutline2;
+
+ 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
+
+ // deactivate pause
+ Paused := false;
+
+ LoadNextSong();
+
+ Log.LogStatus('End', 'OnShow');
+end;
+
+procedure TScreenSing.onShowFinish;
+begin
+ // hide cursor on singscreen show
+ Display.SetCursor;
+
+ // prepare music
+ // Important: AudioPlayback must not be initialized in onShow() as TScreenSong
+ // uses stops AudioPlayback in onHide() which interferes with TScreenSings onShow.
+ AudioPlayback.Open(CurrentSong.Path.Append(CurrentSong.Mp3));
+ if ScreenSong.Mode = smMedley then
+ AudioPlayback.SetVolume(0.1)
+ else
+ AudioPlayback.SetVolume(1.0);
+ AudioPlayback.Position := LyricsState.GetCurrentTime();
+
+ // synchronize music
+ if Ini.SyncTo = Ord(stLyrics) then
+ AudioPlayback.SetSyncSource(fLyricsSync)
+ else
+ AudioPlayback.SetSyncSource(nil);
+
+ // synchronize lyrics (do not set this before AudioPlayback is initialized)
+ if Ini.SyncTo = Ord(stMusic) then
+ LyricsState.SetSyncSource(fMusicSync)
+ else
+ LyricsState.SetSyncSource(nil);
+
+ // start lyrics
+ LyricsState.Start(true);
+
+ // start music
+ if ScreenSong.Mode = smMedley then
+ AudioPlayback.FadeIn(CurrentSong.Medley.FadeIn_time, 1.0)
+ else
+ AudioPlayback.Play();
+
+
+ // start timer
+ CountSkipTimeSet;
+end;
+
+procedure TScreenSing.SongError();
+var
+ I, len: integer;
+
+begin
+ if ScreenSong.Mode <> smMedley then
+ begin
+ // error loading song -> go back to previous screen and show some error message
+ Display.AbortScreenChange;
+ // select new song in party mode
+ if ScreenSong.Mode = smPartyMode then
+ ScreenSong.SelectRandomSong();
+ if Length(CurrentSong.LastError) > 0 then
+ ScreenPopupError.ShowPopup(Format(Language.Translate(CurrentSong.LastError), [CurrentSong.ErrorLineNo]))
+ else
+ ScreenPopupError.ShowPopup(Language.Translate('ERROR_CORRUPT_SONG'));
+ // FIXME: do we need this?
+ CurrentSong.Path := CatSongs.Song[CatSongs.Selected].Path;
+ Exit;
+ end
+ else
+ begin
+ if PlaylistMedley.CurrentMedleySong < PlaylistMedley.NumMedleySongs then
+ begin
+ //Error Loading Song in Medley Mode -> skip actual Medley Song an go on if possible
+ len := Length(PlaylistMedley.Song);
+ for I := PlaylistMedley.CurrentMedleySong-1 to len - 1 do
+ PlaylistMedley.Song[I] := PlaylistMedley.Song[I+1];
+
+ SetLength(PlaylistMedley.Song, Len-1);
+ Dec(PlaylistMedley.NumMedleySongs);
+ LoadNextSong;
+ Exit;
+ end
+ else
+ begin
+ if PlaylistMedley.NumMedleySongs = 1 then
+ begin
+ //Error Loading Song in Medley Mode -> Go back to Song Screen and Show some Error Message
+ Display.AbortScreenChange;
+ // select new song in party mode
+ if ScreenSong.Mode = smPartyMode then
+ ScreenSong.SelectRandomSong();
+ if Length(CurrentSong.LastError) > 0 then
+ ScreenPopupError.ShowPopup(Format(Language.Translate(CurrentSong.LastError), [CurrentSong.ErrorLineNo]))
+ else
+ ScreenPopupError.ShowPopup(Language.Translate('ERROR_CORRUPT_SONG'));
+ // FIXME: do we need this?
+ CurrentSong.Path := CatSongs.Song[CatSongs.Selected].Path;
+ Exit;
+ end
+ else
+ begin
+ //Error Loading Song in Medley Mode -> Finish actual round
+ len := Length(PlaylistMedley.Song);
+ SetLength(PlaylistMedley.Song, len-1);
+ Dec(PlaylistMedley.NumMedleySongs);
+ Finish;
+ Exit;
+ end;
+ end;
+ end;
+end;
+
+procedure TScreenSing.LoadNextSong();
+var
+ Color: TRGB;
+ Index: integer;
+ VideoFile: IPath;
+ BgFile: IPath;
+ success: boolean;
+
+ function FindNote(beat: integer): TPos;
+ var
+ line: integer;
+ note: integer;
+ found: boolean;
+ min: integer;
+ diff: integer;
+
+ begin
+ found := false;
+
+ for line := 0 to length(Lines[0].Line) - 1 do
+ begin
+ for note := 0 to length(Lines[0].Line[line].Note) - 1 do
+ begin
+ if (beat >= Lines[0].Line[line].Note[line].Start) and
+ (beat <= Lines[0].Line[line].Note[line].Start + Lines[0].Line[line].Note[note].Length) then
+ begin
+ Result.part := 0;
+ Result.line := line;
+ Result.note := note;
+ found:=true;
+ break;
+ end;
+ end;
+ end;
+
+ if found then //found exactly
+ exit;
+
+ min := high(integer);
+ //second try (approximating)
+ for line := 0 to length(Lines[0].Line) - 1 do
+ begin
+ for note := 0 to length(Lines[0].Line[line].Note) - 1 do
+ begin
+ diff := abs(Lines[0].Line[line].Note[note].Start - beat);
+ if diff < min then
+ begin
+ Result.part := 0;
+ Result.line := line;
+ Result.note := note;
+ min := diff;
+ end;
+ end;
+ end;
+ end;
+
+begin
+ // reset video playback engine
+ fCurrentVideo := nil;
+
+ // 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 Index := 0 to PlayersPlay - 1 do
+ begin
+ Scores.AddPlayer(Tex_ScoreBG[Index], Color);
+ end;
+
+ Scores.Init; // get positions for players
// FIXME: sets path and filename to ''
ResetSingTemp;
+ PlaylistMedley.ApplausePlayed := false;
+
+ if ScreenSong.Mode = smMedley then
+ begin
+ if length(PlaylistMedley.Song) >= PlaylistMedley.CurrentMedleySong then
+ begin
+ CatSongs.Selected := PlaylistMedley.Song[PlaylistMedley.CurrentMedleySong-1];
+ //Music.Open(CatSongs.Song[CatSongs.Selected].Path + CatSongs.Song[CatSongs.Selected].Mp3);
+ end
+ else
+ begin
+ SongError;
+ Exit;
+ end;
+ end;
+
CurrentSong := CatSongs.Song[CatSongs.Selected];
// FIXME: bad style, put the try-except into loadsong() and not here
@@ -485,27 +752,40 @@ begin
if CurrentSong.FileName.GetExtension.ToUTF8 = '.xml' then
success := CurrentSong.AnalyseXML and CurrentSong.LoadXMLSong()
else
- success := CurrentSong.Analyse and CurrentSong.LoadSong();
+ success := CurrentSong.Analyse; // and CurrentSong.LoadSong();
except
success := false;
end;
if (not success) then
begin
- // error loading song -> go back to previous screen and show some error message
- Display.AbortScreenChange;
- // select new song in party mode
- if ScreenSong.Mode = smPartyMode then
- ScreenSong.SelectRandomSong();
- if (Length(CurrentSong.LastError) > 0) then
- ScreenPopupError.ShowPopup(Format(Language.Translate(CurrentSong.LastError), [CurrentSong.ErrorLineNo]))
- else
- ScreenPopupError.ShowPopup(Language.Translate('ERROR_CORRUPT_SONG'));
- // FIXME: do we need this?
- CurrentSong.Path := CatSongs.Song[CatSongs.Selected].Path;
+ SongError();
Exit;
end;
+ // Set up Medley timings
+ if ScreenSong.Mode = smMedley then
+ begin
+ CurrentSong.SetMedleyMode();
+
+{ ** ToDo
+ Text[SongNameText].Text := IntToStr(PlaylistMedley.CurrentMedleySong) +
+ '/' + IntToStr(PlaylistMedley.NumMedleySongs) + ': ' +
+ CurrentSong.Artist + ' - ' + CurrentSong.Title;
+}
+ //medley start and end timestamps
+ StartNote := FindNote(CurrentSong.Medley.StartBeat - round(CurrentSong.BPM[0].BPM*CurrentSong.Medley.FadeIn_time / 60));
+ MedleyStart := GetTimeFromBeat(Lines[0].Line[StartNote.line].Note[0].Start);
+
+ //check Medley-Start
+ if MedleyStart+CurrentSong.Medley.FadeIn_time * 0.5 > GetTimeFromBeat(CurrentSong.Medley.StartBeat) then
+ MedleyStart := GetTimeFromBeat(CurrentSong.Medley.StartBeat) - CurrentSong.Medley.FadeIn_time;
+ if MedleyStart < 0 then
+ MedleyStart := 0;
+
+ MedleyEnd := GetTimeFromBeat(CurrentSong.Medley.EndBeat) + CurrentSong.Medley.FadeOut_time;
+ end;
+
{*
* == Background ==
* We have four types of backgrounds:
@@ -529,7 +809,10 @@ begin
if (fVideoClip <> nil) then
begin
fShowVisualization := false;
- fCurrentVideo.Position := CurrentSong.VideoGAP + CurrentSong.Start;
+ if ScreenSong.Mode = smMedley then
+ fCurrentVideo.Position := CurrentSong.VideoGAP + MedleyStart
+ else
+ fCurrentVideo.Position := CurrentSong.VideoGAP + CurrentSong.Start;
fCurrentVideo.Play;
end;
end;
@@ -572,28 +855,31 @@ begin
begin
fShowVisualization := true;
fCurrentVideo := Visualization.Open(PATH_NONE);
- if (fCurrentVideo <> nil) then
+ if fCurrentVideo <> nil then
fCurrentVideo.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();
- BadPlayer := AudioInputProcessor.CheckPlayersConfig(PlayersPlay);
- if (BadPlayer <> 0) then
+ if ScreenSong.Mode = smMedley then
begin
- ScreenPopupError.ShowPopup(
- Format(Language.Translate('ERROR_PLAYER_NO_DEVICE_ASSIGNMENT'),
- [BadPlayer]));
+ LyricsState.SetCurrentTime(MedleyStart);
+ LyricsState.StartTime := CurrentSong.Gap;
+ LyricsState.TotalTime := MedleyEnd;
+ end
+ else
+ begin
+ LyricsState.SetCurrentTime(CurrentSong.Start);
+ LyricsState.StartTime := CurrentSong.Gap;
+ if CurrentSong.Finish > 0 then
+ LyricsState.TotalTime := CurrentSong.Finish / 1000
+ else
+ LyricsState.TotalTime := AudioPlayback.Length;
end;
+ LyricsState.UpdateBeats();
+
// prepare and start voice-capture
AudioInput.CaptureStart;
@@ -619,51 +905,6 @@ begin
// main text
Lyrics.Clear(CurrentSong.BPM[0].BPM, CurrentSong.Resolution);
- // set custom options
- case Ini.LyricsFont of
- 0: // normal fonts
- begin
- Lyrics.FontStyle := ftNormal;
-
- 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 := 0.02;
- Lyrics.LineColor_act.G := 0.6;
- Lyrics.LineColor_act.B := 0.8;
- Lyrics.LineColor_act.A := 1;
- end;
- 1, 2: // outline fonts
- begin
- if (Ini.LyricsFont = 1) then
- Lyrics.FontStyle := ftOutline1
- else
- Lyrics.FontStyle := ftOutline2;
-
- 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
@@ -671,9 +912,6 @@ begin
Lyrics.AddLine(@Lines[0].Line[Lyrics.LineCounter]);
end;
- // deactivate pause
- Paused := false;
-
// kill all stars not killed yet (goldenstarstwinkle mod)
GoldenRec.SentenceChange;
@@ -684,51 +922,18 @@ begin
if Lines[0].Line[Index].TotalNotes = 0 then
Inc(NumEmptySentences);
- eSongLoaded.CallHookChain(False);
+ eSongLoaded.CallHookChain(false);
- Log.LogStatus('End', 'OnShow');
-end;
-
-procedure TScreenSing.onShowFinish;
-begin
- // hide cursor on singscreen show
- Display.SetCursor;
-
- // prepare music
- // Important: AudioPlayback must not be initialized in onShow() as TScreenSong
- // uses stops AudioPlayback in onHide() which interferes with TScreenSings onShow.
- AudioPlayback.Open(CurrentSong.Path.Append(CurrentSong.Mp3));
- AudioPlayback.SetVolume(1.0);
- AudioPlayback.Position := CurrentSong.Start;
-
- // synchronize music
- if (Ini.SyncTo = Ord(stLyrics)) then
- AudioPlayback.SetSyncSource(fLyricsSync)
- else
- AudioPlayback.SetSyncSource(nil);
-
- // synchronize lyrics (do not set this before AudioPlayback is initialized)
- if (Ini.SyncTo = Ord(stMusic)) then
- LyricsState.SetSyncSource(fMusicSync)
- else
- LyricsState.SetSyncSource(nil);
-
- // start lyrics
- LyricsState.Start(true);
-
- // start music
- AudioPlayback.Play();
-
- // start timer
- CountSkipTimeSet;
+ if (ScreenSong.Mode = smMedley) and (PlaylistMedley.CurrentMedleySong > 1) then
+ onShowFinish;
end;
procedure TScreenSing.ClearSettings;
begin
- Settings.Finish := False;
- Settings.LyricsVisible := True;
- Settings.NotesVisible := high(Integer);
- Settings.PlayerEnabled := high(Integer);
+ Settings.Finish := false;
+ Settings.LyricsVisible := true;
+ Settings.NotesVisible := high(integer);
+ Settings.PlayerEnabled := high(integer);
end;
{ applies changes of settings record }
@@ -739,13 +944,13 @@ end;
procedure TScreenSing.EndSong;
begin
- Settings.Finish := True;
+ Settings.Finish := true;
end;
procedure TScreenSing.OnHide;
begin
// background texture
- if (Tex_Background.TexNum > 0) then
+ if Tex_Background.TexNum > 0 then
begin
glDeleteTextures(1, PGLuint(@Tex_Background.TexNum));
Tex_Background.TexNum := 0;
@@ -757,15 +962,19 @@ end;
function TScreenSing.Draw: boolean;
var
- DisplayTime: real;
- DisplayPrefix: string;
- DisplayMin: integer;
- DisplaySec: integer;
- T: integer;
- CurLyricsTime: real;
- VideoFrameTime: Extended;
- Line: TLyricLine;
- LastWord: TLyricWord;
+ DisplayTime: real;
+ DisplayPrefix: string;
+ DisplayMin: integer;
+ DisplaySec: integer;
+ T: integer;
+ CurLyricsTime: real;
+ TotalTime: real;
+ VideoFrameTime: extended;
+ Line: TLyricLine;
+ LastWord: TLyricWord;
+ medley_end: boolean;
+ medley_start_applause: boolean;
+
begin
Background.Draw;
@@ -805,19 +1014,28 @@ begin
// retrieve current lyrics time, we have to store the value to avoid
// that min- and sec-values do not match
- CurLyricsTime := LyricsState.GetCurrentTime();
+ if ScreenSong.Mode = smMedley then
+ begin
+ CurLyricsTime := LyricsState.GetCurrentTime() - ScreenSing.MedleyStart;
+ TotalTime := ScreenSing.MedleyEnd - ScreenSing.MedleyStart;
+ end
+ else
+ begin
+ CurLyricsTime := LyricsState.GetCurrentTime();
+ TotalTime := LyricsState.TotalTime;
+ end;
// retrieve time for timebar text
case (fTimebarMode) of
tbmRemaining: begin
- DisplayTime := LyricsState.TotalTime - CurLyricsTime;
+ DisplayTime := TotalTime - CurLyricsTime;
DisplayPrefix := '-';
end;
tbmTotal: begin
- DisplayTime := LyricsState.TotalTime;
+ DisplayTime := TotalTime;
DisplayPrefix := '#';
end;
- else begin
+ else begin // current time
DisplayTime := CurLyricsTime;
DisplayPrefix := '';
end;
@@ -838,6 +1056,19 @@ begin
SungToEnd := true;
end;
+ // for medley-mode:
+ CurLyricsTime := LyricsState.GetCurrentTime();
+ if (ScreenSong.Mode = smMedley) and (CurLyricsTime > MedleyEnd) then
+ medley_end := true
+ else
+ medley_end := false;
+
+ if (ScreenSong.Mode = smMedley) and (CurLyricsTime >
+ GetTimeFromBeat(CurrentSong.Medley.EndBeat)) then
+ medley_start_applause := true
+ else
+ medley_start_applause := false;
+
// update and draw movie
if Assigned(fCurrentVideo) then
begin
@@ -866,28 +1097,35 @@ begin
// draw static menu (FG)
DrawFG;
+ //Medley Countdown
+ if ScreenSong.Mode = smMedley then
+ DrawMedleyCountdown;
+
// 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)) and
- (not Settings.Finish) then
+ if (not AudioPlayback.Finished) and (not medley_end or (ScreenSong.Mode <> smMedley)) and
+ ((CurrentSong.Finish = 0) or (LyricsState.GetCurrentTime()*1000 <= CurrentSong.Finish)) and
+ (not Settings.Finish) then
begin
// analyze song if not paused
if (not Paused) then
begin
Sing(Self);
+
+ //Update Medley Stats
+ if (ScreenSong.Mode = smMedley) and not FadeOut then
+ UpdateMedleyStats(medley_start_applause);
+
Party.CallOnSing;
end;
end
else
begin
- if (not FadeOut) and (Screens=1) or (ScreenAct=2) then
+ if (not FadeOut) and (Screens = 1) or (ScreenAct = 2) then
begin
Finish;
- FadeOut := true;
end;
end;
end;
@@ -915,6 +1153,10 @@ begin
end;
procedure TScreenSing.Finish;
+var
+ I, J: integer;
+ len, num: integer;
+
begin
AudioInput.CaptureStop;
AudioPlayback.Stop;
@@ -944,34 +1186,134 @@ begin
SetFontItalic(false);
- if not FadeOut then
- Party.CallAfterSing;
+ if ScreenSong.Mode = smMedley then
+ begin
+ if not FadeOut then
+ begin
+ for I := 0 to PlayersPlay - 1 do
+ PlaylistMedley.Stats[Length(PlaylistMedley.Stats) - 1].Player[I] := Player[I];
+
+ Inc(PlaylistMedley.CurrentMedleySong);
+ if PlaylistMedley.CurrentMedleySong <= PlaylistMedley.NumMedleySongs then
+ begin
+ LoadNextSong;
+ end
+ else
+ begin
+ //build sums
+ len := Length(PlaylistMedley.Stats);
+ num := PlaylistMedley.NumPlayer;
+
+ SetLength(PlaylistMedley.Stats, len + 1);
+ SetLength(PlaylistMedley.Stats[len].Player, num);
+
+ for J := 0 to len - 1 do
+ begin
+ for I := 0 to num - 1 do
+ begin
+ PlaylistMedley.Stats[len].Player[I].Score :=
+ PlaylistMedley.Stats[len].Player[I].Score +
+ PlaylistMedley.Stats[J].Player[I].Score;
+
+ PlaylistMedley.Stats[len].Player[I].ScoreLine :=
+ PlaylistMedley.Stats[len].Player[I].ScoreLine +
+ PlaylistMedley.Stats[J].Player[I].ScoreLine;
+
+ PlaylistMedley.Stats[len].Player[I].ScoreGolden :=
+ PlaylistMedley.Stats[len].Player[I].ScoreGolden +
+ PlaylistMedley.Stats[J].Player[I].ScoreGolden;
+
+ PlaylistMedley.Stats[len].Player[I].ScoreInt :=
+ PlaylistMedley.Stats[len].Player[I].ScoreInt +
+ PlaylistMedley.Stats[J].Player[I].ScoreInt;
+
+ PlaylistMedley.Stats[len].Player[I].ScoreLineInt :=
+ PlaylistMedley.Stats[len].Player[I].ScoreLineInt +
+ PlaylistMedley.Stats[J].Player[I].ScoreLineInt;
+
+ PlaylistMedley.Stats[len].Player[I].ScoreGoldenInt :=
+ PlaylistMedley.Stats[len].Player[I].ScoreGoldenInt +
+ PlaylistMedley.Stats[J].Player[I].ScoreGoldenInt;
+
+ PlaylistMedley.Stats[len].Player[I].ScoreTotalInt :=
+ PlaylistMedley.Stats[len].Player[I].ScoreTotalInt +
+ PlaylistMedley.Stats[J].Player[I].ScoreTotalInt;
+ end; //of for I
+ end; //of for J
+
+ //build mean on sum
+ for I := 0 to num - 1 do
+ begin
+ PlaylistMedley.Stats[len].Player[I].Score := round(
+ PlaylistMedley.Stats[len].Player[I].Score / len);
+
+ PlaylistMedley.Stats[len].Player[I].ScoreLine := round(
+ PlaylistMedley.Stats[len].Player[I].ScoreLine / len);
+
+ PlaylistMedley.Stats[len].Player[I].ScoreGolden := round(
+ PlaylistMedley.Stats[len].Player[I].ScoreGolden / len);
+
+ PlaylistMedley.Stats[len].Player[I].ScoreInt := round(
+ PlaylistMedley.Stats[len].Player[I].ScoreInt / len);
+
+ PlaylistMedley.Stats[len].Player[I].ScoreLineInt := round(
+ PlaylistMedley.Stats[len].Player[I].ScoreLineInt / len);
+
+ PlaylistMedley.Stats[len].Player[I].ScoreGoldenInt := round(
+ PlaylistMedley.Stats[len].Player[I].ScoreGoldenInt / len);
+
+ PlaylistMedley.Stats[len].Player[I].ScoreTotalInt := round(
+ PlaylistMedley.Stats[len].Player[I].ScoreTotalInt / len);
+ end;
+
+ Party.CallAfterSing;
+ FadeOut:=true;
+ end;
+ end;
+ end
+ else
+ begin
+ SetLength(PlaylistMedley.Stats, 1);
+ SetLength(PlaylistMedley.Stats[0].Player, PlayersPlay);
+ for I := 0 to PlayersPlay - 1 do
+ PlaylistMedley.Stats[0].Player[I] := Player[I];
+
+ PlaylistMedley.Stats[0].SongArtist := CurrentSong.Artist;
+ PlaylistMedley.Stats[0].SongTitle := CurrentSong.Title;
+
+ if not FadeOut then
+ Party.CallAfterSing;
+
+ FadeOut := true;
+ end;
end;
procedure TScreenSing.OnSentenceEnd(SentenceIndex: cardinal);
var
- PlayerIndex: byte;
- CurrentPlayer: PPLayer;
- CurrentScore: real;
- Line: PLine;
+ PlayerIndex: byte;
+ 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
+ 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
+ if Line.TotalNotes <= 0 then
Exit;
// set max song score
- if (Ini.LineBonus = 0) then
+ if Ini.LineBonus = 0 then
MaxSongScore := MAX_SONG_SCORE
else
MaxSongScore := MAX_SONG_SCORE - MAX_SONG_LINE_BONUS;
@@ -990,7 +1332,7 @@ begin
LineScore := CurrentScore - CurrentPlayer.ScoreLast;
// check for lines with low points
- if (MaxLineScore <= 2) then
+ if MaxLineScore <= 2 then
LinePerfection := 1
else
// determine LinePerfection
@@ -999,13 +1341,13 @@ begin
LinePerfection := LineScore / (MaxLineScore - 2);
// clamp LinePerfection to range [0..1]
- if (LinePerfection < 0) then
+ if LinePerfection < 0 then
LinePerfection := 0
- else if (LinePerfection > 1) then
+ else if LinePerfection > 1 then
LinePerfection := 1;
// add line-bonus if enabled
- if (Ini.LineBonus > 0) then
+ 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) -
@@ -1028,7 +1370,7 @@ begin
Scores.RaiseScore(PlayerIndex, CurrentPlayer.ScoreTotalInt);
// PerfectLineTwinkle (effect), part 1
- if (Ini.EffectSing = 1) then
+ if Ini.EffectSing = 1 then
CurrentPlayer.LastSentencePerfect := (LinePerfection >= 1);
// refresh last score
@@ -1036,7 +1378,7 @@ begin
end;
// PerfectLineTwinkle (effect), part 2
- if (Ini.EffectSing = 1) then
+ if Ini.EffectSing = 1 then
GoldenRec.SpawnPerfectLineTwinkle;
end;
@@ -1052,7 +1394,7 @@ begin
(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
+ if Lyrics.LineCounter <= High(Lines[0].Line) then
Lyrics.AddLine(@Lines[0].Line[Lyrics.LineCounter])
else
Lyrics.AddLine(nil);
@@ -1069,5 +1411,60 @@ begin
Result := AudioPlayback.Position;
end;
-end.
+procedure TScreenSing.UpdateMedleyStats(medley_end: boolean);
+var
+ len, num, I: integer;
+
+begin
+ len := Length(PlaylistMedley.Stats);
+ num := PlaylistMedley.NumPlayer;
+
+ if (PlaylistMedley.CurrentMedleySong > len) and
+ (PlaylistMedley.CurrentMedleySong <= PlaylistMedley.NumMedleySongs) then
+ begin
+ inc(len);
+ SetLength(PlaylistMedley.Stats, len);
+ SetLength(PlaylistMedley.Stats[len - 1].Player, num);
+ PlaylistMedley.Stats[len-1].SongArtist := CurrentSong.Artist;
+ PlaylistMedley.Stats[len-1].SongTitle := CurrentSong.Title;
+ end;
+
+ if PlaylistMedley.CurrentMedleySong <= PlaylistMedley.NumMedleySongs then
+ for I := 0 to num - 1 do
+ PlaylistMedley.Stats[len - 1].Player[I] := Player[I];
+
+ if medley_end and not PlaylistMedley.ApplausePlayed and
+ (PlaylistMedley.CurrentMedleySong <= PlaylistMedley.NumMedleySongs) then
+ begin
+ PlaylistMedley.ApplausePlayed := true;
+ end;
+end;
+procedure TScreenSing.DrawMedleyCountdown();
+var
+ w, h: real;
+ timeDiff: real;
+ t: real;
+ CountDownText: UTF8String;
+
+begin
+ if AudioPlayback.Position < GetTimeFromBeat(CurrentSong.Medley.StartBeat) then
+ begin
+ timeDiff := GetTimeFromBeat(CurrentSong.Medley.StartBeat)-AudioPlayback.Position+1;
+ t := frac(timeDiff);
+
+ glColor4f(0.15, 0.30, 0.6, t);
+
+ h := 300 * t * ScreenH / RenderH;
+ SetFontStyle(ftBold);
+ SetFontItalic(false);
+ SetFontSize(h);
+ CountDownText := IntToStr(round(timeDiff - t));
+ w := glTextWidth(PChar(CountDownText));
+
+ SetFontPos (RenderW / 2 - w / 2, RenderH / 2 - h / 2);
+ glPrint(PChar(CountDownText));
+ end;
+end;
+
+end.
diff --git a/src/screens/UScreenSong.pas b/src/screens/UScreenSong.pas
index 6fe8d204..7558a59b 100644
--- a/src/screens/UScreenSong.pas
+++ b/src/screens/UScreenSong.pas
@@ -38,7 +38,6 @@ uses
SDL,
UCommon,
UDisplay,
- UPath,
UFiles,
UIni,
ULanguage,
@@ -46,6 +45,7 @@ uses
UMenu,
UMenuEqualizer,
UMusic,
+ UPath,
USong,
USongs,
UTexture,
@@ -53,6 +53,8 @@ uses
UTime;
type
+ TVisArr = array of integer;
+
TScreenSong = class(TMenu)
private
Equalizer: Tms_Equalizer;
@@ -161,6 +163,10 @@ type
//Extensions
procedure DrawExtensions;
+
+ //Medley
+ procedure StartMedley(NumSongs: integer; MinSource: TMedleySource);
+ function getVisibleMedleyArr(MinSource: TMedleySource): TVisArr;
end;
implementation
@@ -188,7 +194,7 @@ var
begin
if CatSongs.VisibleSongs > 0 then
begin
- I2:= 0;
+ I2 := 0;
for I := Low(CatSongs.Song) to High(Catsongs.Song) do
begin
if CatSongs.Song[I].Visible then
@@ -209,7 +215,7 @@ var
begin
if CatSongs.VisibleSongs > 0 then
begin
- I2:= 0;
+ I2 := 0;
for I := Low(CatSongs.Song) to High(Catsongs.Song) do
begin
if CatSongs.Song[I].Visible then
@@ -260,9 +266,9 @@ function TScreenSong.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; Presse
var
I: integer;
I2: integer;
- SDL_ModState: word;
- UpperLetter: UCS4Char;
- TempStr: UTF8String;
+ SDL_ModState: word;
+ UpperLetter: UCS4Char;
+ TempStr: UTF8String;
begin
Result := true;
@@ -417,6 +423,39 @@ begin
Exit;
end;
+ Ord('S'):
+ begin
+ Log.LogError('SDL_ModState: ' + inttostr(SDL_ModState));
+ Log.LogError('KMOD_LSHIFT: ' + inttostr(KMOD_LSHIFT));
+ Log.LogError('CatSongs.Song[Interaction].Medley.Source: ' + inttostr(ord(CatSongs.Song[Interaction].Medley.Source)));
+ Log.LogError('msCalculated: ' + inttostr(ord(msCalculated)));
+ Log.LogError('msTag: ' + inttostr(ord(msTag)));
+ Log.LogError('Mode: ' + inttostr(ord(Mode)));
+ Log.LogError('smNormal: ' + inttostr(ord(smNormal)));
+ if //(SDL_ModState = KMOD_LSHIFT) and
+ (CatSongs.Song[Interaction].Medley.Source >= msCalculated) and (Mode = smNormal) then
+ StartMedley(0, msCalculated)
+ else if (CatSongs.Song[Interaction].Medley.Source >= msTag) and (Mode = smNormal) then
+ StartMedley(0, msTag);
+ end;
+
+ Ord('D'):
+ begin
+ Log.LogError('SDL_ModState: ' + inttostr(SDL_ModState));
+ Log.LogError('KMOD_LSHIFT: ' + inttostr(KMOD_LSHIFT));
+ Log.LogError('length(getVisibleMedleyArr(msCalculated)): ' + inttostr(length(getVisibleMedleyArr(msCalculated))));
+ Log.LogError('msCalculated: ' + inttostr(ord(msCalculated)));
+ Log.LogError('msTag: ' + inttostr(ord(msTag)));
+ Log.LogError('Length(getVisibleMedleyArr(msTag)): ' + inttostr(Length(getVisibleMedleyArr(msTag))));
+ Log.LogError('smNormal: ' + inttostr(ord(smNormal)));
+ Log.LogError('Mode: ' + inttostr(ord(Mode)));
+ if (Mode = smNormal) and //(SDL_ModState = KMOD_LSHIFT) and
+ (length(getVisibleMedleyArr(msCalculated)) > 0) then
+ StartMedley(5, msCalculated)
+ else if (Mode = smNormal) and (Length(getVisibleMedleyArr(msTag)) > 0) then
+ StartMedley(5, msTag);
+ end;
+
Ord('R'):
begin
if (Songs.SongList.Count > 0) and
@@ -1518,8 +1557,15 @@ begin
// reset video playback engine
fCurrentVideo := nil;
- if Ini.Players <= 3 then PlayersPlay := Ini.Players + 1;
- if Ini.Players = 4 then PlayersPlay := 6;
+ // reset Medley-Playlist
+ SetLength(PlaylistMedley.Song, 0);
+ if Mode = smMedley then
+ Mode := smNormal;
+
+ if Ini.Players <= 3 then
+ PlayersPlay := Ini.Players + 1;
+ if Ini.Players = 4 then
+ PlayersPlay := 6;
//Cat Mod etc
if (Ini.TabsAtStartup = 1) and (CatSongs.CatNumShow = -1) then
@@ -1536,21 +1582,21 @@ begin
end;
//Playlist Mode
- if (Mode = smNormal) then
+ if Mode = smNormal then
begin
//If Playlist Shown -> Select Next automatically
- if (CatSongs.CatNumShow = -3) then
+ if CatSongs.CatNumShow = -3 then
begin
SelectNext;
end;
end
//Party Mode
- else if (Mode = smPartyMode) then
+ else if Mode = smPartyMode then
begin
SelectRandomSong;
//Show Menu directly in PartyMode
//But only if selected in Options
- if (Ini.PartyPopup = 1) then
+ if Ini.PartyPopup = 1 then
begin
ScreenSongMenu.MenuShow(SM_Party_Main);
end;
@@ -1810,14 +1856,18 @@ begin
begin
PreviewOpened := Interaction;
- PreviewPos := AudioPlayback.Length / 4;
+ if Song.PreviewStart > 0 then
+ PreviewPos := Song.PreviewStart
+ else
+ PreviewPos := AudioPlayback.Length / 4;
+
// fix for invalid music file lengths
- if (PreviewPos > 60.0) then
+ if (PreviewPos > 60.0) and (Song.PreviewStart = 0) then
PreviewPos := 60.0;
AudioPlayback.Position := PreviewPos;
// set preview volume
- if (Ini.PreviewFading = 0) then
+ if Ini.PreviewFading = 0 then
begin
// music fade disabled: start with full volume
AudioPlayback.SetVolume(IPreviewVolumeVals[Ini.PreviewVolume]);
@@ -2153,4 +2203,131 @@ begin
}
end;
+procedure TScreenSong.StartMedley(NumSongs: integer; MinSource: TMedleySource);
+ procedure AddSong(SongNr: integer);
+ begin
+ SetLength(PlaylistMedley.Song, Length(PlaylistMedley.Song) + 1);
+ PlaylistMedley.Song[Length(PlaylistMedley.Song) - 1] := SongNr;
+ end;
+
+ function SongAdded(SongNr: integer): boolean;
+ var
+ i: integer;
+ skipped: boolean;
+ begin
+ skipped := false;
+ for i := 0 to Length(PlaylistMedley.Song) - 1 do
+ begin
+ if SongNr = PlaylistMedley.Song[i] then
+ begin
+ skipped := true;
+ break;
+ end;
+ end;
+ Result := skipped;
+ end;
+
+ function NumSongsAdded(): Integer;
+ begin
+ Result := Length(PlaylistMedley.Song);
+ end;
+
+ function GetNextSongNr(MinS: TMedleySource): integer;
+ var
+ I, num: integer;
+ unused_arr: array of integer;
+ visible_arr: TVisArr;
+ begin
+ SetLength(unused_arr, 0);
+ visible_arr := getVisibleMedleyArr(MinS);
+ for I := 0 to Length(visible_arr) - 1 do
+ begin
+ if (not SongAdded(visible_arr[I])) then
+ begin
+ SetLength(unused_arr, Length(unused_arr)+1);
+ unused_arr[Length(unused_arr) - 1] := visible_arr[I];
+ end;
+ end;
+
+ num := Random(Length(unused_arr));
+ Result := unused_arr[num];
+end;
+
+var
+ I: integer;
+ VS: integer;
+
+begin
+ if NumSongs > 0 then
+ begin
+ VS := Length(getVisibleMedleyArr(MinSource));
+ if VS < NumSongs then
+ PlaylistMedley.NumMedleySongs := VS
+ else
+ PlaylistMedley.NumMedleySongs := NumSongs;
+
+ //set up Playlist Medley
+ SetLength(PlaylistMedley.Song, 0);
+ for I := 0 to PlaylistMedley.NumMedleySongs - 1 do
+ begin
+ AddSong(GetNextSongNr(MinSource));
+ end;
+ end
+ else //start this song
+ begin
+ SetLength(PlaylistMedley.Song, 1);
+ PlaylistMedley.Song[0] := Interaction;
+ PlaylistMedley.NumMedleySongs := 1;
+ end;
+
+ if Mode = smNormal then
+ begin
+ Mode := smMedley;
+ StopMusicPreview();
+
+ //TODO: how about case 2? menu for medley mode?
+ case Ini.OnSongClick of
+ 0: FadeTo(@ScreenSing);
+ 1: SelectPlayers;
+ 2: FadeTo(@ScreenSing);
+ {2: begin
+ if CatSongs.CatNumShow = -3 then
+ ScreenSongMenu.MenuShow(SM_Playlist)
+ else
+ ScreenSongMenu.MenuShow(SM_Main);
+ end;}
+ end;
+ end;
+end;
+
+function TScreenSong.getVisibleMedleyArr(MinSource: TMedleySource): TVisArr;
+var
+ I: integer;
+
+begin
+ SetLength(Result, 0);
+ if CatSongs.Song[Interaction].Main then
+ begin
+ for I := 0 to Length(CatSongs.Song) - 1 do
+ begin
+ if not CatSongs.Song[I].Main and (CatSongs.Song[I].Medley.Source >= MinSource) then
+ begin
+ SetLength(Result, Length(Result) + 1);
+ Result[Length(Result) - 1] := I;
+ end;
+ end;
+ end
+ else
+ begin
+ for I := 0 to Length(CatSongs.Song) - 1 do
+ begin
+ if CatSongs.Song[I].Visible and (CatSongs.Song[I].Medley.Source >= MinSource) then
+ begin
+ SetLength(Result, Length(Result) + 1);
+ Result[Length(Result)-1] := I;
+ end;
+ end;
+ end;
+end;
+
end.