From 665d1f930ea1f543f6c7b3a2fe6609735e8effb1 Mon Sep 17 00:00:00 2001
From: brunzelchen <brunzelchen@b956fd51-792f-4845-bead-9b4dfca2ff2c>
Date: Wed, 9 Dec 2009 19:29:14 +0000
Subject: added medley code changes

git-svn-id: svn://svn.code.sf.net/p/ultrastardx/svn/branches/experimental@2013 b956fd51-792f-4845-bead-9b4dfca2ff2c
---
 Medley/game/themes/Deluxe.ini       |  18 ++
 Medley/src/base/UDraw.pas           |   9 +-
 Medley/src/base/UNote.pas           |  36 +++-
 Medley/src/base/USong.pas           | 329 ++++++++++++++++++++++++++++-
 Medley/src/base/USongs.pas          |  19 +-
 Medley/src/base/UThemes.pas         |  16 +-
 Medley/src/screens/UScreenScore.pas | 241 ++++++++++++++--------
 Medley/src/screens/UScreenSing.pas  | 399 +++++++++++++++++++++++++++++++-----
 Medley/src/screens/UScreenSong.pas  | 150 +++++++++++++-
 9 files changed, 1058 insertions(+), 159 deletions(-)

diff --git a/Medley/game/themes/Deluxe.ini b/Medley/game/themes/Deluxe.ini
index cf3ad368..d7ac8adb 100644
--- a/Medley/game/themes/Deluxe.ini
+++ b/Medley/game/themes/Deluxe.ini
@@ -991,6 +991,24 @@ Size = 18
 Color = White
 Align = 1
 
+[SingSongNameStatic]
+X = 5
+Y = 3
+W = 790
+H = 34
+Tex = Button
+Color = White
+Type = Transparen
+
+[SingSongNameText]
+Text = should not see me
+X = 400
+Y = 5
+Font = 1
+Size = 30
+Color = Black
+Align = 1
+
 # O N E  P L A Y E R   M O D E # # # # # # # # # # # # # # # # # # # #
 #PlayerOne
 [SingP1Static]
diff --git a/Medley/src/base/UDraw.pas b/Medley/src/base/UDraw.pas
index 1783986f..8bb2c210 100644
--- a/Medley/src/base/UDraw.pas
+++ b/Medley/src/base/UDraw.pas
@@ -97,6 +97,7 @@ uses
   URecord,
   UScreenSing,
   UScreenSingModi,
+  USong,
   UTexture;
 
 procedure SingDrawBackground;
@@ -1387,7 +1388,13 @@ begin
     if (CurLyricsTime > 0) and
        (LyricsState.TotalTime > 0) then
     begin
-      LyricsProgress := CurLyricsTime / LyricsState.TotalTime;
+      if ScreenSong.Mode <> smMedley then
+        LyricsProgress := CurLyricsTime / LyricsState.TotalTime
+      else
+        LyricsProgress := (CurLyricsTime - GetTimeFromBeat(CurrentSong.Medley.StartBeat) +
+          CurrentSong.Medley.FadeIn_time) / (GetTimeFromBeat(CurrentSong.Medley.EndBeat) +
+          CurrentSong.Medley.FadeOut_time - GetTimeFromBeat(CurrentSong.Medley.StartBeat) +
+          CurrentSong.Medley.FadeIn_time);
       glTexCoord2f((width * LyricsProgress) / 8, 0);
       glVertex2f(x + width * LyricsProgress, y);
 
diff --git a/Medley/src/base/UNote.pas b/Medley/src/base/UNote.pas
index 8e5b709a..bc91c892 100644
--- a/Medley/src/base/UNote.pas
+++ b/Medley/src/base/UNote.pas
@@ -88,13 +88,30 @@ type
     Note:           array of TPlayerNote;
   end;
 
+  TStats = record
+    Player: array of TPlayer;
+    SongArtist:   UTF8String;
+    SongTitle:    UTF8String;
+  end;
+
+  TMedleyPlaylist = record
+    Song:               array of integer;
+    NumMedleySongs:     integer;
+    CurrentMedleySong:  integer;
+    ApplausePlayed:     boolean;
+    Stats:              array of TStats;
+    NumPlayer:          integer;
+  end;
+
 var
 
   // player and music info
   Player:      array of TPlayer;
   PlayersPlay: integer;
-
+  PlaylistMedley: TMedleyPlaylist;
   CurrentSong: TSong;
+  max_song_score_medley: integer;
+  max_song_line_bonus_medley: integer;
 
 const
   MAX_SONG_SCORE = 10000;     // max. achievable points per song
@@ -463,10 +480,19 @@ begin
             // half size notes patch
             NoteHit := true;
 
-            if (Ini.LineBonus > 0) then
-              MaxSongPoints := MAX_SONG_SCORE - MAX_SONG_LINE_BONUS
-            else
-              MaxSongPoints := MAX_SONG_SCORE;
+            if ScreenSong.Mode <> smMedley then
+            begin
+              if (Ini.LineBonus > 0) then
+                MaxSongPoints := MAX_SONG_SCORE - MAX_SONG_LINE_BONUS
+              else
+                MaxSongPoints := MAX_SONG_SCORE;
+            end else
+            begin
+              if (Ini.LineBonus > 0) then
+                MaxSongPoints := max_song_score_medley - max_song_line_bonus_medley
+              else
+                MaxSongPoints := max_song_score_medley;
+            end;
 
             // Note: ScoreValue is the sum of all note values of the song
             // (MaxSongPoints / ScoreValue) is the points that a player
diff --git a/Medley/src/base/USong.pas b/Medley/src/base/USong.pas
index c465f198..d4eb714d 100644
--- a/Medley/src/base/USong.pas
+++ b/Medley/src/base/USong.pas
@@ -64,7 +64,8 @@ uses
 
 type
 
-  TSingMode = ( smNormal, smPartyMode, smPlaylistRandom );
+  TSingMode = ( smNormal, smPartyMode, smPlaylistRandom, smMedley );
+  TMedleySource = ( msNone, msCalculated, msTag );
 
   TBPM = record
     BPM:        real;
@@ -85,6 +86,16 @@ type
     Content: UTF8String;
   end;
 
+  TMedley = record
+    Source:       TMedleySource;  //source of the information
+    StartBeat:    integer;  //start beat of medley
+    EndBeat:      integer;  //end beat of medley
+    FadeIn:       integer;  //start beat of fadein
+    FadeOut:      integer;  //end beat of fadeout
+    FadeIn_time:  real;     //FadeIn-Time in seconds
+    FadeOut_time: real;     //FadeIn-Time in seconds
+  end;
+
   TSong = class
   private
     FileLineNo  : integer;  // line, which is read last, for error reporting
@@ -157,6 +168,9 @@ type
     MultBPM : integer;
 
     LastError: AnsiString;
+
+    Medley:   TMedley;   //infos for medley-mode and preview start
+
     function  GetErrorLineNo: integer;
     property  ErrorLineNo: integer read GetErrorLineNo;
 
@@ -168,6 +182,9 @@ type
     function    Analyse(const ReadCustomTags: Boolean = false): boolean;
     function    AnalyseXML(): boolean;
     procedure   Clear();
+    procedure   FindRefrainStart;
+    procedure   SetMedleyMode;
+    function    ReadMedleyFile(MedleyFilePath: IPath): boolean;
   end;
 
 implementation
@@ -178,7 +195,7 @@ uses
   UIni,
   UPathUtils,
   UMusic,  //needed for Lines
-  UNote;   //needed for Player
+  UNote;
 
 const
   DEFAULT_ENCODING = encAuto;
@@ -461,6 +478,7 @@ begin
         Exit;
       end;
 
+      SetLength(Lines, 0);  //just a fix.. for medley-mod
       SetLength(Lines, 2);
       for Count := 0 to High(Lines) do
       begin
@@ -589,7 +607,6 @@ begin
     if (High(Lines[Count].Line) >= 0) then
       Lines[Count].Line[High(Lines[Count].Line)].LastLine := true;
   end;
-
   Result := true;
 end;
 
@@ -1292,6 +1309,7 @@ begin
   Creator    := '';
 
   Relative := false;
+  Medley.Source := msNone;
 end;
 
 function TSong.Analyse(const ReadCustomTags: Boolean): boolean;
@@ -1310,7 +1328,10 @@ begin
     Self.clear;
 
     //Read Header
-    Result := Self.ReadTxTHeader(SongFile, ReadCustomTags)
+    Result := Self.ReadTxTHeader(SongFile, ReadCustomTags);
+
+    //Load Song (for testing)
+    Result := Result and Self.LoadSong;
   finally
     SongFile.Free;
   end;
@@ -1318,7 +1339,6 @@ end;
 
 
 function TSong.AnalyseXML(): boolean;
-
 begin
   Result := false;
 
@@ -1331,6 +1351,305 @@ begin
   //Read Header
   Result := self.ReadXMLHeader( FileName );
 
+  //Load Song (for testing)
+  Result := Result and self.LoadXMLSong;
+
+end;
+
+
+{* new procedure for preview
+   tries find out the beginning of a refrain *}
+procedure TSong.FindRefrainStart();
+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;
+
+begin
+  if Medley.Source = msTag then
+    Exit;
+
+  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
+      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
+  if Length(series)>0 then
+  begin
+    max := 0;
+    for I := 0 to Length(series) - 1 do
+    begin
+      if series[I].len > series[max].len then
+        max := I;
+    end;
+  end;
+
+  if (Length(series)>0) and (series[max].len > 3) then
+  begin
+    Medley.Source := msCalculated;
+    Medley.StartBeat := Lines[0].Line[series[max].start].Note[0].Start;
+  end else
+    Medley.Source := msNone;
+end;
+
+//sets a song to medley-mod:
+//converts all unneeded notes into freestyle
+//updates score values
+procedure TSong.SetMedleyMode;
+var
+  pl, line, note: integer;
+  LF:             TLineFragment;
+  start:          integer;
+  end_:           integer;
+begin
+  start := self.Medley.StartBeat;
+  end_  := self.Medley.EndBeat;
+
+  for pl := 0 to Length(Lines) - 1 do
+  begin
+    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
+        LF := Lines[pl].Line[line].Note[note];
+        if LF.Start < start then      //check start
+          Lines[pl].Line[line].Note[note].NoteType := ntFreestyle
+        else if LF.Start>= end_ then  //check end
+          Lines[pl].Line[line].Note[note].NoteType := ntFreestyle
+        else
+        begin
+          //add this notes value ("notes length" * "notes scorefactor") to the current songs entire value
+          Inc(Lines[pl].ScoreValue, LF.Length * ScoreFactor[LF.NoteType]);
+          //and to the current lines entire value
+          Inc(Lines[pl].Line[line].TotalNotes, LF.Length * ScoreFactor[LF.NoteType]);
+        end;
+      end;
+    end;
+  end;
+end;
+
+//reads the txtm
+//TODO move this to ReadTXTHeader and implement the MEDLEY-TAGS in txt-file:
+//conversion: START-> MEDLEY_START
+//            END-> MEDLEY_END
+//            FADE_IN -> MEDLEY_FADE_IN
+//            FADE_OUT-> MEDLEY_FADE_OUT
+//TODO: write a tool to convert existing txtm
+function TSong.ReadMedleyFile(MedleyFilePath: IPath): boolean;
+const
+  DEFAULT_FADE_IN_TIME = 10;
+  DEFAULT_FADE_OUT_TIME = 4;
+var
+  Line, Identifier: string;
+  Value: string;
+  SepPos: integer; // separator position
+  Done: byte;      // bit-vector of mandatory fields
+  EncFile: IPath; // encoded filename
+  FullFileName: string;
+  LineNo: integer;
+  MedleyFile:  TTextFileStream;
+  found_fadeIn, found_fadeOut: boolean;
+
+begin
+  Result := true;
+  Done   := 0;
+
+  MedleyFile := TMemTextFileStream.Create(MedleyFilePath, fmOpenRead);
+  FullFileName := MedleyFilePath.ToNative;
+
+  //set standard values
+  found_fadeIn := false;
+  found_fadeOut := false;
+
+  //Read first Line
+  MedleyFile.ReadLine(Line);
+  if (Length(Line) <= 0) then
+  begin
+    Log.LogError('File starts with empty line: ' + FullFileName,
+                 'TSong.ReadMedleyFile');
+    Result := false;
+    Exit;
+  end;
+
+  // check if file begins with a UTF-8 BOM, if so set encoding to UTF-8
+  if (CheckReplaceUTF8BOM(Line)) then
+    Encoding := encUTF8;
+
+  //Read Lines while Line starts with # or its empty
+  while (Length(Line) = 0) or (Line[1] = '#') do
+  begin
+    //Increase Line Number
+    Inc (LineNo);
+    SepPos := Pos(':', Line);
+
+    //Line has no Seperator, ignore non header field
+    if (SepPos = 0) then
+    begin
+      // read next line
+      if (not MedleyFile.ReadLine(Line)) then
+      begin
+        Result := false;
+        Log.LogError('File incomplete or not Ultrastar txtm (A): ' + FullFileName);
+        Break;
+      end;
+      Continue;
+    end;
+
+    //Read Identifier and Value
+    Identifier  := UpperCase(Trim(Copy(Line, 2, SepPos - 2))); //Uppercase is for Case Insensitive Checks
+    Value       := Trim(Copy(Line, SepPos + 1, Length(Line) - SepPos));
+
+    //Check the Identifier (If Value is given)
+    if (Length(Value) = 0) then
+    begin
+      Log.LogWarn('Empty field "'+Identifier+'" in file ' + FullFileName,
+                   'TSong.ReadMedleyFile');
+    end
+    else
+    begin
+    
+      //-----------
+      //Required Attributes
+      //-----------
+
+      if (Identifier = 'START') then
+      begin
+        if TryStrtoInt(Value, self.Medley.StartBeat) then
+          //Add START flag to Done
+          Done := Done or 1;
+      end
+
+      else if (Identifier = 'END') then
+      begin
+        if TryStrtoInt(Value, self.Medley.EndBeat) then
+          //Add END Flag to Done
+          Done := Done or 2;
+      end
+
+      //---------
+      //Additional Header Information
+      //---------
+
+      else if (Identifier = 'FADE_IN') then
+      begin
+        if TryStrtoInt(Value, self.Medley.FadeIn) then
+          found_fadeIn := true;
+      end
+
+      else if (Identifier = 'FADE_OUT') then
+      begin
+        if TryStrtoInt(Value, self.Medley.FadeOut) then
+          found_fadeOut := true;
+      end
+    end; // End check for non-empty Value
+
+    // read next line
+    if (not MedleyFile.ReadLine(Line)) then
+    begin
+      Result := false;
+      Log.LogError('File incomplete or not Ultrastar txtm (A): ' + FullFileName);
+      Break;
+    end;
+  end; // while
+
+  //Check if all Required Values are given
+  if (Done <> 3) then
+  begin
+    Result := false;
+    if (Done and 2) = 0 then //No End Flag
+      Log.LogError('END tag missing: ' + FullFileName)
+    else if (Done and 1) = 0 then //No Start Flag
+      Log.LogError('START tag missing: ' + FullFileName)
+    else //unknown Error
+      Log.LogError('File incomplete or not Ultrastar txtm (B - '+ inttostr(Done) +'): ' + FullFileName);
+
+    Self.Medley.Source := msNone;
+  end else if self.Medley.StartBeat>=self.Medley.EndBeat then
+  begin
+    Log.LogError('Failed to load MEDLEY-TAGS (Start>=End): ' + FullFileName);
+    self.Medley.Source := msNone;
+    Result := false;
+  end else if found_fadeIn and (self.Medley.FadeIn>=self.Medley.StartBeat) then
+  begin
+    Log.LogError('Failed to load MEDLEY-TAGS (FadeIn>=Start): ' + FullFileName);
+    self.Medley.Source := msNone;
+    Result := false;
+  end else if found_fadeOut and (self.Medley.EndBeat>=self.Medley.FadeOut) then
+  begin
+    Log.LogError('Failed to load MEDLEY-TAGS (End>=FadeOut): ' + FullFileName);
+    self.Medley.Source := msNone;
+    Result := false;
+  end else
+  begin
+    self.Medley.Source := msTag;
+    CurrentSong := self;
+    if not found_fadeIn then  //set FadeIn if not defined
+    begin                     // TODO: what if FadeIn < song start?
+      self.Medley.FadeIn := self.Medley.StartBeat - round(GetMidBeat(DEFAULT_FADE_IN_TIME));
+    end;
+    if not found_fadeOut then //set FadeOut if not defined
+    begin                     // TODO: what if FadeOut > song end?
+      self.Medley.FadeOut := self.Medley.EndBeat + round(GetMidBeat(DEFAULT_FADE_OUT_TIME));
+    end;
+
+    //calculate fade time
+
+    self.Medley.FadeIn_time := GetTimeFromBeat(CurrentSong.Medley.StartBeat) -
+      GetTimeFromBeat(CurrentSong.Medley.FadeIn);
+    self.Medley.FadeOut_time := GetTimeFromBeat(CurrentSong.Medley.FadeOut) -
+      GetTimeFromBeat(CurrentSong.Medley.EndBeat);
+  end;
 end;
 
 end.
diff --git a/Medley/src/base/USongs.pas b/Medley/src/base/USongs.pas
index baeec13a..af7f7a16 100644
--- a/Medley/src/base/USongs.pas
+++ b/Medley/src/base/USongs.pas
@@ -312,18 +312,33 @@ var
   Files: TPathDynArray;
   Song: TSong;
   Extension: IPath;
+  MedleyFiles:  TPathDynArray;
+  MedleyExtension: IPath;
 begin
   SetLength(Files, 0);
+
   Extension := Path('.txt');
+  MedleyExtension := Path('.txtm');
   FindFilesByExtension(Dir, Extension, true, Files);
 
+
   for I := 0 to High(Files) do
   begin
     Song := TSong.Create(Files[I]);
 
     if Song.Analyse then
-      SongList.Add(Song)
-    else
+    begin
+      //medley support...   TODO: move it (see USong...)
+      SetLength(MedleyFiles, 0);
+      FindFilesByExtension(Files[I].GetPath, MedleyExtension, true, MedleyFiles);
+
+      if Length(MedleyFiles)>0 then
+      begin
+        Song.ReadMedleyFile(MedleyFiles[0]);
+      end;
+
+      SongList.Add(Song);
+    end else
     begin
       Log.LogError('AnalyseFile failed for "' + Files[I].ToNative + '".');
       FreeAndNil(Song);
diff --git a/Medley/src/base/UThemes.pas b/Medley/src/base/UThemes.pas
index 4322815e..6ffeb3b1 100644
--- a/Medley/src/base/UThemes.pas
+++ b/Medley/src/base/UThemes.pas
@@ -306,6 +306,9 @@ type
   end;
 
    TThemeSing = class(TThemeBasic)
+    //Show actual SongName
+    StaticSongName  :   TThemeStatic;
+    TextSongName    :   TThemeText;
 
     //TimeBar mod
     StaticTimeProgress:   TThemeStatic;
@@ -358,7 +361,7 @@ type
     LineBonusText:    array [0..8] of UTF8String;
 
     //Pause Popup
-     PausePopUp:      TThemeStatic;
+    PausePopUp:      TThemeStatic;
   end;
 
   TThemeLyricBar = record
@@ -1051,10 +1054,13 @@ begin
 
       // Sing
       ThemeLoadBasic(Sing, 'Sing');
-      //TimeBar mod
-       ThemeLoadStatic(Sing.StaticTimeProgress, 'SingTimeProgress');
-       ThemeLoadText(Sing.TextTimeText, 'SingTimeText');
-      //eoa TimeBar mod
+      ThemeLoadStatic(Sing.StaticSongName, 'SingSongNameStatic');
+      ThemeLoadText(Sing.TextSongName, 'SingSongNameText');
+
+    //TimeBar mod
+      ThemeLoadStatic(Sing.StaticTimeProgress, 'SingTimeProgress');
+      ThemeLoadText(Sing.TextTimeText, 'SingTimeText');
+    //eoa TimeBar mod
 
     //moveable singbar mod
       ThemeLoadStatic(Sing.StaticP1SingBar, 'SingP1SingBar');
diff --git a/Medley/src/screens/UScreenScore.pas b/Medley/src/screens/UScreenScore.pas
index ce1b11e5..5a50a3dc 100644
--- a/Medley/src/screens/UScreenScore.pas
+++ b/Medley/src/screens/UScreenScore.pas
@@ -39,6 +39,7 @@ uses
   SysUtils,
   UDisplay,
   UMusic,
+  USong,
   USongs,
   UThemes,
   gl,
@@ -74,19 +75,30 @@ type
     BarGolden_ActualHeight: real;
   end;
 
+  TPlayerScoreData = record
+    Data: array[1..6] of TPlayerScoreScreenData;
+  end;
+
   TPlayerScoreRatingPics = record               // a fine array of the rating pictures
     RateEaseStep:  integer;
     RateEaseValue: real;
   end;
 
+  TPLayerScorePics = record
+    Data: array[1..6] of TPlayerScoreRatingPics;
+  end;
+
   TScreenScore = class(TMenu)
     private
       BarTime:            cardinal;
       ArrayStartModifier: integer;
     public
+      //TeamInfo:   TTeamInfo;
       aPlayerScoreScreenTextures: array[1..6] of TPlayerScoreScreenTexture;
-      aPlayerScoreScreenDatas:    array[1..6] of TPlayerScoreScreenData;
-      aPlayerScoreScreenRatings:  array[1..6] of TPlayerScoreRatingPics;
+      aPlayerScoreScreenDatas:    array of TPlayerScoreData;
+      aPlayerScoreScreenRatings:  array of TPlayerScorePics;
+
+      ActualRound: integer;
 
       BarScore_EaseOut_Step:  real;
       BarPhrase_EaseOut_Step: real;
@@ -133,6 +145,7 @@ type
       procedure OnShow; override;
       procedure OnShowFinish; override;
       function Draw: boolean; override;
+      procedure RefreshTexts;
       procedure FillPlayer(Item, P: integer);
 
       procedure EaseBarIn(PlayerNumber: integer; BarType: string);
@@ -190,6 +203,22 @@ begin
         begin
           Display.SaveScreenShot;
         end;
+      SDLK_RIGHT:
+        begin
+          if ActualRound<Length(PlaylistMedley.Stats)-1 then
+          begin
+            inc(ActualRound);
+            RefreshTexts;
+          end;
+        end;
+      SDLK_LEFT:
+        begin
+          if ActualRound>0 then
+          begin
+            dec(ActualRound);
+            RefreshTexts;
+          end;
+        end;
     end;
   end;
 end;
@@ -203,6 +232,37 @@ begin
   end;
 end;
 
+procedure TScreenScore.RefreshTexts;
+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;
+end;
+
 constructor TScreenScore.Create;
 var
   Player:  integer;
@@ -293,20 +353,21 @@ begin
   else
     ArrayStartModifier := 0; //this should never happen
   end;
+  ActualRound:=0;
+  SetLength(aPlayerScoreScreenDatas, Length(PlaylistMedley.Stats));
+  SetLength(aPlayerScoreScreenRatings, Length(PlaylistMedley.Stats));
 
-  for P := 1 to PlayersPlay do
+  for I := 0 to Length(PlaylistMedley.Stats) - 1 do
   begin
-    // data
-    aPlayerScoreScreenDatas[P].Bar_Y                  := Theme.Score.StaticBackLevel[P + ArrayStartModifier].Y;
-
-    // ratings
-    aPlayerScoreScreenRatings[P].RateEaseStep         := 1;
-    aPlayerScoreScreenRatings[P].RateEaseValue        := 20;
+    for P := 1 to PlayersPlay do
+    begin
+      aPlayerScoreScreenDatas[I].Data[P].Bar_Y :=
+        Theme.Score.StaticBackLevel[P + ArrayStartModifier].Y;
+      aPlayerScoreScreenRatings[I].Data[P].RateEaseStep := 1;
+      aPlayerScoreScreenRatings[I].Data[P].RateEaseValue := 20;
+    end;
   end;
-
-  Text[TextArtist].Text      := CurrentSong.Artist;
-  Text[TextTitle].Text       := CurrentSong.Title;
-  Text[TextArtistTitle].Text := CurrentSong.Artist + ' - ' + CurrentSong.Title;
+  RefreshTexts;
 
   // set visibility
   case PlayersPlay of
@@ -405,18 +466,6 @@ var
   PStart:        integer;
   PHigh:         integer;
 begin
-{*
-  player[0].ScoreInt       := 7000;
-  player[0].ScoreLineInt   := 2000;
-  player[0].ScoreGoldenInt := 1000;
-  player[0].ScoreTotalInt  := 10000;
-
-  player[1].ScoreInt       := 2500;
-  player[1].ScoreLineInt   := 1100;
-  player[1].ScoreGoldenInt :=  900;
-  player[1].ScoreTotalInt  := 4500;
-*}
-
   //Draw the Background
   DrawBG;
 
@@ -548,7 +597,8 @@ begin
   Text[TextNotesScore[ThemeIndex]].Alpha              := (BarScore_EaseOut_Step / 100);
 
   // total score
-  Text[TextTotalScore[ThemeIndex]].Text               := IntToStr(TextScore_ActualValue[PlayerNumber] + TextPhrase_ActualValue[PlayerNumber] + TextGolden_ActualValue[PlayerNumber]);
+  Text[TextTotalScore[ThemeIndex]].Text               := IntToStr(TextScore_ActualValue[PlayerNumber] +
+    TextPhrase_ActualValue[PlayerNumber] + TextGolden_ActualValue[PlayerNumber]);
   Text[TextTotalScore[ThemeIndex]].Alpha              := (BarScore_EaseOut_Step / 100);
 
   Text[TextTotal[ThemeIndex]].Alpha                   := (BarScore_EaseOut_Step / 100);
@@ -562,9 +612,13 @@ begin
 end;
 
 procedure TScreenScore.ShowRating(PlayerNumber: integer);
+const
+  rate_factor: array[0..7] of real = (2.0, 4.0, 5.0, 6.0, 7.5, 8.5, 9.0, 10.0);
 var
   Rating: integer;
   ThemeIndex: integer;
+  rate_max: array[0..7] of integer;
+  max, I: integer;
 begin
 
   // We have to do this here because we use the same Theme Object
@@ -575,55 +629,57 @@ begin
     6: ThemeIndex := ((PlayerNumber-1) mod 3) + 1 + ArrayStartModifier;
   end;
 
-  case (Player[PlayerNumber-1].ScoreTotalInt) of
-   0..2009:
-     begin
-       Text[TextScore[ThemeIndex]].Text := Language.Translate('SING_SCORE_TONE_DEAF');
-       Rating := 0;
-     end;
-   2010..4009:
-     begin
-       Text[TextScore[ThemeIndex]].Text := Language.Translate('SING_SCORE_AMATEUR');
-       Rating := 1;
-     end;
-   4010..5009:
-     begin
-       Text[TextScore[ThemeIndex]].Text := Language.Translate('SING_SCORE_WANNABE');
-       Rating := 2;
-     end;
-   5010..6009:
-     begin
-       Text[TextScore[ThemeIndex]].Text := Language.Translate('SING_SCORE_HOPEFUL');
-       Rating := 3;
-     end;
-   6010..7509:
-     begin
-       Text[TextScore[ThemeIndex]].Text := Language.Translate('SING_SCORE_RISING_STAR');
-       Rating := 4;
-     end;
-   7510..8509:
-     begin
-       Text[TextScore[ThemeIndex]].Text := Language.Translate('SING_SCORE_LEAD_SINGER');
-       Rating := 5;
-     end;
-   8510..9009:
-     begin
-       Text[TextScore[ThemeIndex]].Text := Language.Translate('SING_SCORE_SUPERSTAR');
-       Rating := 6;
-     end;
-   9010..10000:
-     begin
-       Text[TextScore[ThemeIndex]].Text := Language.Translate('SING_SCORE_ULTRASTAR');
-       Rating := 7;
-     end;
+  //build rating scores
+  if ActualRound = Length(PlaylistMedley.Stats)-1 then
+    max := MAX_SONG_SCORE
   else
+    max := max_song_score_medley;
+
+  for I := 0 to 6 do
+    rate_max[I] := round(max/10*rate_factor[I])+9;
+
+  //fix 7
+  rate_max[7] := 10000;
+
+  if PlaylistMedley.Stats[ActualRound].Player[PlayerNumber-1].ScoreTotalInt<=rate_max[0] then
+  begin
+    Text[TextScore[ThemeIndex]].Text := Language.Translate('SING_SCORE_TONE_DEAF');
+    Rating := 0;
+  end else if PlaylistMedley.Stats[ActualRound].Player[PlayerNumber-1].ScoreTotalInt<=rate_max[1] then
+  begin
+    Text[TextScore[ThemeIndex]].Text := Language.Translate('SING_SCORE_AMATEUR');
+    Rating := 1;
+  end else if PlaylistMedley.Stats[ActualRound].Player[PlayerNumber-1].ScoreTotalInt<=rate_max[2] then
+  begin
+    Text[TextScore[ThemeIndex]].Text := Language.Translate('SING_SCORE_WANNABE');
+    Rating := 2;
+  end else if PlaylistMedley.Stats[ActualRound].Player[PlayerNumber-1].ScoreTotalInt<=rate_max[3] then
+  begin
+    Text[TextScore[ThemeIndex]].Text := Language.Translate('SING_SCORE_HOPEFUL');
+    Rating := 3;
+  end else if PlaylistMedley.Stats[ActualRound].Player[PlayerNumber-1].ScoreTotalInt<=rate_max[4] then
+  begin
+    Text[TextScore[ThemeIndex]].Text := Language.Translate('SING_SCORE_RISING_STAR');
+    Rating := 4;
+  end else if PlaylistMedley.Stats[ActualRound].Player[PlayerNumber-1].ScoreTotalInt<=rate_max[5] then
+  begin
+    Text[TextScore[ThemeIndex]].Text := Language.Translate('SING_SCORE_LEAD_SINGER');
+    Rating := 5;
+  end else if PlaylistMedley.Stats[ActualRound].Player[PlayerNumber-1].ScoreTotalInt<=rate_max[6] then
+  begin
+    Text[TextScore[ThemeIndex]].Text := Language.Translate('SING_SCORE_SUPERSTAR');
+    Rating := 6;
+  end else if PlaylistMedley.Stats[ActualRound].Player[PlayerNumber-1].ScoreTotalInt<=rate_max[7] then
+  begin
+    Text[TextScore[ThemeIndex]].Text := Language.Translate('SING_SCORE_ULTRASTAR');
+    Rating := 7;
+  end else
     Rating := 0; // Cheata :P
-  end;
 
   //todo: this could break if the width is not given, for instance when there's a skin with no picture for ratings
-  if ( Theme.Score.StaticRatings[ThemeIndex].W > 0 ) and  ( aPlayerScoreScreenRatings[PlayerNumber].RateEaseValue > 0 ) then
+  if ( Theme.Score.StaticRatings[ThemeIndex].W > 0 ) and  ( aPlayerScoreScreenRatings[ActualRound].Data[PlayerNumber].RateEaseValue > 0 ) then
   begin
-    Text[TextScore[ThemeIndex]].Alpha := aPlayerScoreScreenRatings[PlayerNumber].RateEaseValue / Theme.Score.StaticRatings[ThemeIndex].W;
+    Text[TextScore[ThemeIndex]].Alpha := aPlayerScoreScreenRatings[ActualRound].Data[PlayerNumber].RateEaseValue / Theme.Score.StaticRatings[ThemeIndex].W;
   end;
   // end todo
 
@@ -642,7 +698,7 @@ begin
   PosX := Theme.Score.StaticRatings[PlayerNumber + ArrayStartModifier].X + (Theme.Score.StaticRatings[PlayerNumber + ArrayStartModifier].W  * 0.5);
   PosY := Theme.Score.StaticRatings[PlayerNumber + ArrayStartModifier].Y + (Theme.Score.StaticRatings[PlayerNumber + ArrayStartModifier].H  * 0.5); ;
 
-  Width := aPlayerScoreScreenRatings[PlayerNumber].RateEaseValue/2;
+  Width := aPlayerScoreScreenRatings[ActualRound].Data[PlayerNumber].RateEaseValue/2;
 
   glBindTexture(GL_TEXTURE_2D, Tex_Score_Ratings[Rating].TexNum);
 
@@ -669,7 +725,7 @@ var
   RaiseStep, MaxVal: real;
   EaseOut_Step:      integer;
 begin
-  EaseOut_Step  := aPlayerScoreScreenRatings[PlayerNumber].RateEaseStep;
+  EaseOut_Step  := aPlayerScoreScreenRatings[ActualRound].Data[PlayerNumber].RateEaseStep;
   MaxVal        := Theme.Score.StaticRatings[PlayerNumber + ArrayStartModifier].W;
 
   RaiseStep     := EaseOut_Step;
@@ -688,8 +744,8 @@ begin
     s           := p/(2*PI) * arcsin (1);
     ReturnValue := MaxVal * power(2,-5 * RaiseStep) * sin( (RaiseStep * MaxVal - s) * (2 * PI) / p) + MaxVal;
 
-    inc(aPlayerScoreScreenRatings[PlayerNumber].RateEaseStep);
-    aPlayerScoreScreenRatings[PlayerNumber].RateEaseValue := ReturnValue;
+    inc(aPlayerScoreScreenRatings[ActualRound].Data[PlayerNumber].RateEaseStep);
+    aPlayerScoreScreenRatings[ActualRound].Data[PlayerNumber].RateEaseValue := ReturnValue;
   end;
 
   Result := ReturnValue;
@@ -716,21 +772,21 @@ begin
   // EaseOut_Step is the actual step in the raising process, like the 20iest step of EaseOut_MaxSteps
   if (BarType = 'Note') then
   begin
-    Score        := Player[PlayerNumber - 1].ScoreInt;
+    Score        := PlaylistMedley.Stats[ActualRound].Player[PlayerNumber - 1].ScoreInt;
     RaiseStep    := BarScore_EaseOut_Step;
     BarStartPosY := Theme.Score.StaticBackLevel[PlayerNumber + ArrayStartModifier].Y + MaxHeight;
   end
   else if (BarType = 'Line') then
   begin
-    Score        := Player[PlayerNumber - 1].ScoreLineInt;
+    Score        := PlaylistMedley.Stats[ActualRound].Player[PlayerNumber - 1].ScoreLineInt;
     RaiseStep    := BarPhrase_EaseOut_Step;
-    BarStartPosY := Theme.Score.StaticBackLevel[PlayerNumber + ArrayStartModifier].Y - aPlayerScoreScreenDatas[PlayerNumber].BarScore_ActualHeight + MaxHeight;
+    BarStartPosY := Theme.Score.StaticBackLevel[PlayerNumber + ArrayStartModifier].Y - aPlayerScoreScreenDatas[ActualRound].Data[PlayerNumber].BarScore_ActualHeight + MaxHeight;
   end
   else if (BarType = 'Golden') then
   begin
-    Score        := Player[PlayerNumber - 1].ScoreGoldenInt;
+    Score        := PlaylistMedley.Stats[ActualRound].Player[PlayerNumber - 1].ScoreGoldenInt;
     RaiseStep    := BarGolden_EaseOut_Step;
-    BarStartPosY := Theme.Score.StaticBackLevel[PlayerNumber + ArrayStartModifier].Y - aPlayerScoreScreenDatas[PlayerNumber].BarScore_ActualHeight - aPlayerScoreScreenDatas[PlayerNumber].BarLine_ActualHeight + MaxHeight;
+    BarStartPosY := Theme.Score.StaticBackLevel[PlayerNumber + ArrayStartModifier].Y - aPlayerScoreScreenDatas[ActualRound].Data[PlayerNumber].BarScore_ActualHeight - aPlayerScoreScreenDatas[ActualRound].Data[PlayerNumber].BarLine_ActualHeight + MaxHeight;
   end
   else
   begin
@@ -739,9 +795,12 @@ begin
   end;
 
   // the height dependend of the score
-  Height2Reach := (Score / MAX_SONG_SCORE) * MaxHeight;
+  if ActualRound=Length(PlaylistMedley.Stats)-1 then
+    Height2Reach := (Score / MAX_SONG_SCORE) * MaxHeight
+  else
+    Height2Reach := (Score / max_song_score_medley) * MaxHeight;
 
-  if (aPlayerScoreScreenDatas[PlayerNumber].Bar_Actual_Height < Height2Reach) then
+  if (aPlayerScoreScreenDatas[ActualRound].Data[PlayerNumber].Bar_Actual_Height < Height2Reach) then
   begin
     // Check http://proto.layer51.com/d.aspx?f=400 for more info on easing functions
     // Calculate the actual step according to the maxsteps
@@ -761,11 +820,11 @@ begin
   DrawBar(BarType, PlayerNumber, BarStartPosY, NewHeight);
 
   if (BarType = 'Note') then
-    aPlayerScoreScreenDatas[PlayerNumber].BarScore_ActualHeight  := NewHeight
+    aPlayerScoreScreenDatas[ActualRound].Data[PlayerNumber].BarScore_ActualHeight  := NewHeight
   else if (BarType = 'Line') then
-    aPlayerScoreScreenDatas[PlayerNumber].BarLine_ActualHeight   := NewHeight
+    aPlayerScoreScreenDatas[ActualRound].Data[PlayerNumber].BarLine_ActualHeight   := NewHeight
   else if (BarType = 'Golden') then
-    aPlayerScoreScreenDatas[PlayerNumber].BarGolden_ActualHeight := NewHeight;
+    aPlayerScoreScreenDatas[ActualRound].Data[PlayerNumber].BarGolden_ActualHeight := NewHeight;
 end;
 
 procedure TscreenScore.DrawBar(BarType: string; PlayerNumber: integer; BarStartPosY: single; NewHeight: real);
@@ -839,19 +898,19 @@ begin
   begin
     EaseOut_Step     := BarScore_EaseOut_Step;
     ActualScoreValue := TextScore_ActualValue[PlayerNumber];
-    ScoreReached     := Player[PlayerNumber-1].ScoreInt;
+    ScoreReached     := PlaylistMedley.Stats[ActualRound].Player[PlayerNumber-1].ScoreInt;
   end;
   if (ScoreType = 'Line') then
   begin
     EaseOut_Step     := BarPhrase_EaseOut_Step;
     ActualScoreValue := TextPhrase_ActualValue[PlayerNumber];
-    ScoreReached     := Player[PlayerNumber-1].ScoreLineInt;
+    ScoreReached     := PlaylistMedley.Stats[ActualRound].Player[PlayerNumber-1].ScoreLineInt;
   end;
   if (ScoreType = 'Golden') then
   begin
     EaseOut_Step     := BarGolden_EaseOut_Step;
     ActualScoreValue := TextGolden_ActualValue[PlayerNumber];
-    ScoreReached     := Player[PlayerNumber-1].ScoreGoldenInt;
+    ScoreReached     := PlaylistMedley.Stats[ActualRound].Player[PlayerNumber-1].ScoreGoldenInt;
   end;
 
   // EaseOut_Step is the actual step in the raising process, like the 20iest step of EaseOut_MaxSteps
@@ -893,7 +952,7 @@ var
 begin
   Text[TextName[Item]].Text := Ini.Name[P];
 
-  S := IntToStr((Round(Player[P].Score) div 10) * 10);
+  S := IntToStr((Round(PlaylistMedley.Stats[ActualRound].Player[P].Score) div 10) * 10);
   while (Length(S)<4) do
     S := '0' + S;
   Text[TextNotesScore[Item]].Text := S;
@@ -903,17 +962,17 @@ begin
 
   //fixed: line bonus and golden notes don't show up,
   //       another bug: total score was shown without added golden-, linebonus
-  S := IntToStr(Player[P].ScoreTotalInt);
+  S := IntToStr(PlaylistMedley.Stats[ActualRound].Player[P].ScoreTotalInt);
   while (Length(S)<5) do
     S := '0' + S;
   Text[TextTotalScore[Item]].Text := S;
 
-  S := IntToStr(Player[P].ScoreLineInt);
+  S := IntToStr(PlaylistMedley.Stats[ActualRound].Player[P].ScoreLineInt);
   while (Length(S)<4) do
     S := '0' + S;
   Text[TextLineBonusScore[Item]].Text := S;
 
-  S := IntToStr(Player[P].ScoreGoldenInt);
+  S := IntToStr(PlaylistMedley.Stats[ActualRound].Player[P].ScoreGoldenInt);
   while (Length(S)<4) do
     S := '0' + S;
   Text[TextGoldenNotesScore[Item]].Text := S;
diff --git a/Medley/src/screens/UScreenSing.pas b/Medley/src/screens/UScreenSing.pas
index 342abac1..1bc16754 100644
--- a/Medley/src/screens/UScreenSing.pas
+++ b/Medley/src/screens/UScreenSing.pas
@@ -46,10 +46,12 @@ uses
   UMenu,
   UMusic,
   USingScores,
+  USong,
   USongs,
   UTexture,
   UThemes,
   UPath,
+  UPathUtils,
   UTime;
 
 type
@@ -95,6 +97,11 @@ type
     FadeOut: boolean;
     Lyrics:  TLyricEngine;
 
+    SongNameStatic: integer;
+    SongNameText: integer;
+
+    ApplauseSounds: array of TAudioPlaybackStream;
+
     // score manager:
     Scores: TSingScores;
 
@@ -112,6 +119,8 @@ type
     function ParseInput(PressedKey: cardinal; CharCode: UCS4Char;
       PressedDown: boolean): boolean; override;
     function Draw: boolean; override;
+    procedure LoadNextSong;
+    procedure UpdateMedleyStats(medley_end: boolean);
 
     procedure Finish; virtual;
     procedure Pause; // toggle pause
@@ -130,7 +139,6 @@ uses
   ULanguage,
   UNote,
   URecord,
-  USong,
   UDisplay,
   UUnicodeUtils;
 
@@ -149,7 +157,14 @@ begin
       begin
         // when not ask before exit then finish now
         if (Ini.AskbeforeDel <> 1) then
-          Finish
+        begin
+          if ScreenSong.Mode=smMedley then
+            PlaylistMedley.CurrentMedleySong:=PlaylistMedley.NumMedleySongs+1;
+          Finish;
+          AudioPlayback.PlaySound(SoundLib.Back);
+          FadeOut := true;
+          FadeTo(@ScreenScore);
+        end
         // else just pause and let the popup make the work
         else if not Paused then
           Pause;
@@ -185,9 +200,11 @@ begin
       begin
         // record sound hack:
         //Sound[0].BufferLong
-
+        if ScreenSong.Mode=smMedley then
+          PlaylistMedley.CurrentMedleySong:=PlaylistMedley.NumMedleySongs+1;
         Finish;
         AudioPlayback.PlaySound(SoundLib.Back);
+        FadeOut := true;
         FadeTo(@ScreenScore);
       end;
 
@@ -307,11 +324,18 @@ begin
   // <note> pausepopup is not visibile at the beginning </note>
   Static[StaticPausePopup].Visible := false;
 
+  SongNameStatic := AddStatic(Theme.Sing.StaticSongName);
+  SongNameText := AddText(Theme.Sing.TextSongName);
+
   Lyrics := TLyricEngine.Create(
       Theme.LyricBar.UpperX, Theme.LyricBar.UpperY, Theme.LyricBar.UpperW, Theme.LyricBar.UpperH,
       Theme.LyricBar.LowerX, Theme.LyricBar.LowerY, Theme.LyricBar.LowerW, Theme.LyricBar.LowerH);
 
   LyricsSync := TLyricsSyncSource.Create();
+
+  SetLength(ApplauseSounds, 1);
+  FreeAndNil(ApplauseSounds[0]);
+  ApplauseSounds[0] := AudioPlayback.OpenSound(SoundPath.Append('Applause.mp3'));
 end;
 
 procedure TScreenSing.OnShow;
@@ -334,6 +358,19 @@ begin
 
   //the song was sung to the end
   SungToEnd := false;
+  //Reset Player Medley stats
+  if ScreenSong.Mode = smMedley then
+  begin
+    PlaylistMedley.CurrentMedleySong:=1;
+    PlaylistMedley.ApplausePlayed := false;
+
+    //max_song_score_medley := round(MAX_SONG_SCORE / NumMedleySongs);
+    //max_song_line_bonus_medley := round(MAX_SONG_LINE_BONUS / NumMedleySongs);
+    PlaylistMedley.NumPlayer := PlayersPlay;
+    SetLength(PlaylistMedley.Stats, 0);
+    max_song_score_medley := round(MAX_SONG_SCORE / PlaylistMedley.NumMedleySongs);
+    max_song_line_bonus_medley := round(MAX_SONG_LINE_BONUS / PlaylistMedley.NumMedleySongs);
+  end;
 
   // reset video playback engine, to play video clip ...
   fCurrentVideoPlaybackEngine := VideoPlayback;
@@ -425,10 +462,43 @@ begin
   Static[StaticP3R].Visible := V3R;
   Text[TextP3R].Visible     := V3R;
 
+  if ScreenSong.Mode = smMedley then
+  begin
+    Static[SongNameStatic].Visible := true;
+    Text[SongNameText].Visible := true;
+  end else
+  begin
+    Static[SongNameStatic].Visible := false;
+    Text[SongNameText].Visible := false;
+  end;
+
+  LoadNextSong;
+
+  Log.LogStatus('End', 'OnShow');
+end;
+
+procedure TScreenSing.LoadNextSong;
+var
+  Index:  integer;
+  VideoFile, BgFile: IPath;
+  success: boolean;
+
+begin
   // FIXME: sets path and filename to ''
+  //AudioPlayback.Stop();
   ResetSingTemp;
-
-  CurrentSong := CatSongs.Song[CatSongs.Selected];
+  
+  if ScreenSong.Mode <> smMedley then
+    CurrentSong := CatSongs.Song[CatSongs.Selected]
+  else
+  begin
+    CurrentSong := CatSongs.Song[PlaylistMedley.Song[PlaylistMedley.CurrentMedleySong-1]];
+    {AudioPlayback.Open(CurrentSong[CatSongsMedley.Selected].Path.Append(CatSongsMedley.Song[CatSongsMedley.Selected].Mp3));
+    CurrentSong := CatSongsMedley.Song[CatSongsMedley.Selected];
+    Text[SongNameText].Text := 'Medley ' + IntToStr(CurrentMedleySong)+'/'+
+      IntToStr(NumMedleySongs)+': '+
+      CurrentSong.Artist+' - '+CurrentSong.Title;}
+  end;
 
   // FIXME: bad style, put the try-except into loadsong() and not here
   try
@@ -445,6 +515,7 @@ begin
   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();
@@ -453,10 +524,17 @@ begin
     else
       ScreenPopupError.ShowPopup(Language.Translate('ERROR_CORRUPT_SONG'));
     // FIXME: do we need this?
-    CurrentSong.Path := CatSongs.Song[CatSongs.Selected].Path;
+    //CurrentSong.Path := CatSongs.Song[CatSongs.Selected].Path;
     Exit;
   end;
 
+  if ScreenSong.Mode = smMedley then
+  begin
+    CurrentSong.SetMedleyMode;
+    Text[SongNameText].Text := IntToStr(PlaylistMedley.CurrentMedleySong) +
+      '/' + IntToStr(PlaylistMedley.NumMedleySongs) + ': ' +
+      CurrentSong.Artist + ' - ' + CurrentSong.Title;
+  end;
   // reset video playback engine, to play video clip ...
   fCurrentVideoPlaybackEngine.Close;
   fCurrentVideoPlaybackEngine := VideoPlayback;
@@ -484,7 +562,12 @@ begin
     begin
       fShowVisualization := false;
       fCurrentVideoPlaybackEngine := VideoPlayback;
-      fCurrentVideoPlaybackEngine.Position := CurrentSong.VideoGAP + CurrentSong.Start;
+      if ScreenSong.Mode <> smMedley then
+        fCurrentVideoPlaybackEngine.Position := CurrentSong.VideoGAP + CurrentSong.Start
+      else
+        fCurrentVideoPlaybackEngine.Position := CurrentSong.VideoGAP +
+          GetTimeFromBeat(CurrentSong.Medley.StartBeat) - CurrentSong.Medley.FadeIn_time +
+          CurrentSong.Start;
       fCurrentVideoPlaybackEngine.Play;
       VideoLoaded := true;
     end;
@@ -534,17 +617,34 @@ begin
 
   // 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();
+  if ScreenSong.Mode <> smMedley then
+  begin
+    LyricsState.SetCurrentTime(CurrentSong.Start);   //in seconds
+    LyricsState.StartTime := CurrentSong.Gap;        //in milliseconds
+    if (CurrentSong.Finish > 0) then
+      LyricsState.TotalTime := CurrentSong.Finish / 1000  //in seconds
+    else
+      LyricsState.TotalTime := AudioPlayback.Length;
+    LyricsState.UpdateBeats();
 
-  // prepare music
-  AudioPlayback.Stop();
-  AudioPlayback.Position := CurrentSong.Start;
+    // prepare music
+    AudioPlayback.Stop();
+    AudioPlayback.Position := CurrentSong.Start;
+  end else
+  begin
+    LyricsState.SetCurrentTime(GetTimeFromBeat(CurrentSong.Medley.StartBeat) - CurrentSong.Medley.FadeIn_time);
+    LyricsState.StartTime := CurrentSong.Gap;
+    if (CurrentSong.Finish > 0) then
+      LyricsState.TotalTime := CurrentSong.Finish / 1000  //in seconds
+    else
+      LyricsState.TotalTime := AudioPlayback.Length;
+    LyricsState.UpdateBeats();
+
+    // prepare music
+    AudioPlayback.Stop();
+    AudioPlayback.Open(CurrentSong.Path.Append(CurrentSong.Mp3));
+    AudioPlayback.Position := GetTimeFromBeat(CurrentSong.Medley.StartBeat) - CurrentSong.Medley.FadeIn_time;
+  end;
   // synchronize music to the lyrics
   AudioPlayback.SetSyncSource(LyricsSync);
 
@@ -572,7 +672,7 @@ begin
 
   // main text
   Lyrics.Clear(CurrentSong.BPM[0].BPM, CurrentSong.Resolution);
-
+  
   // set custom options
   case Ini.LyricsFont of
     0: // normal fonts
@@ -615,42 +715,82 @@ begin
     end;
   end; // case
 
-  // initialize lyrics by filling its queue
-  while (not Lyrics.IsQueueFull) and
+  if ScreenSong.Mode <> smMedley then
+  begin
+    // 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 Index := Low(Lines[0].Line) to High(Lines[0].Line) do
+      if Lines[0].Line[Index].TotalNotes = 0 then
+        Inc(NumEmptySentences);
+  end else
   begin
-    Lyrics.AddLine(@Lines[0].Line[Lyrics.LineCounter]);
+    // 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 Index := Low(Lines[0].Line) to High(Lines[0].Line) do
+      if Lines[0].Line[Index].TotalNotes = 0 then
+        Inc(NumEmptySentences);
   end;
 
-  // deactivate pause
-  Paused := false;
+  //Test
+  // start lyrics
+  LyricsState.Resume();
 
-  // kill all stars not killed yet (goldenstarstwinkle mod)
-  GoldenRec.SentenceChange;
+  // start music
+  if ScreenSong.Mode <> smMedley then
+    AudioPlayback.Play()
+  else
+  begin
+    AudioPlayback.SetVolume(0.3);
+    AudioPlayback.FadeIn(CurrentSong.Medley.FadeIn_time, 1.0);
+  end;
 
-  // set position of line bonus - line bonus end
-  // set number of empty sentences for line bonus
-  NumEmptySentences := 0;
-  for Index := Low(Lines[0].Line) to High(Lines[0].Line) do
-    if Lines[0].Line[Index].TotalNotes = 0 then
-      Inc(NumEmptySentences);
+  // start timer
+  CountSkipTimeSet;
 
-  Log.LogStatus('End', 'OnShow');
+  PlaylistMedley.ApplausePlayed := false;
 end;
 
 procedure TScreenSing.onShowFinish;
 begin
-  // hide cursor on singscreen show    
+  //hide cursor on singscreen show
   Display.SetCursor;
-  
+
   // start lyrics
-  LyricsState.Resume();
+  //LyricsState.Resume();
 
   // start music
-  AudioPlayback.Play();
+  //AudioPlayback.Play();
 
   // start timer
-  CountSkipTimeSet;
+  //CountSkipTimeSet;
 end;
 
 procedure TScreenSing.OnHide;
@@ -672,6 +812,8 @@ var
   Sec:   integer;
   T:     integer;
   CurLyricsTime: real;
+  medley_end: boolean;
+  medley_start_applause: boolean;
   Line: TLyricLine;
   LastWord: TLyricWord;
 begin
@@ -742,7 +884,12 @@ 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
+    CurLyricsTime := LyricsState.TotalTime - LyricsState.GetCurrentTime()
+  else
+    CurLyricsTime := GetTimeFromBeat(CurrentSong.Medley.EndBeat) +
+     CurrentSong.Medley.FadeOut_time - LyricsState.GetCurrentTime();
+
   Min := Round(CurLyricsTime) div 60;
   Sec := Round(CurLyricsTime) mod 60;
 
@@ -785,24 +932,44 @@ begin
   // draw static menu (FG)
   DrawFG;
 
+  if (ScreenSong.Mode = smMedley) and (LyricsState.GetCurrentTime() >
+    GetTimeFromBeat(CurrentSong.Medley.EndBeat) + CurrentSong.Medley.FadeOut_time) then
+    medley_end := true
+  else
+    medley_end := false;
+
+  if (ScreenSong.Mode = smMedley) and (LyricsState.GetCurrentTime() >
+    GetTimeFromBeat(CurrentSong.Medley.EndBeat)) then
+    medley_start_applause := true
+  else
+    medley_start_applause := false;
+
   // 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
+    if (not AudioPlayback.Finished) and not medley_end and ((CurrentSong.Finish = 0) or
       (LyricsState.GetCurrentTime() * 1000 <= CurrentSong.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);
+      end;
     end
     else
     begin
       if (not FadeOut) then
       begin
         Finish;
-        FadeOut := true;
-        FadeTo(@ScreenScore);
+        if ScreenSong.Mode = smNormal then
+        begin
+          FadeOut := true;
+          FadeTo(@ScreenScore);
+        end;
       end;
     end;
   end;
@@ -850,7 +1017,49 @@ begin
   Result := true;
 end;
 
+procedure TScreenSing.UpdateMedleyStats(medley_end: boolean);
+var
+  len, num, I : integer;
+  lastline: boolean;
+  vol:  real;
+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;
+    AudioPlayback.PlaySound(ApplauseSounds[0]);
+  end;
+
+  if(LyricsState.GetCurrentTime() > GetTimeFromBeat(CurrentSong.Medley.EndBeat)) then
+  begin
+    vol := 1-(LyricsState.GetCurrentTime() - GetTimeFromBeat(CurrentSong.Medley.EndBeat))/
+      CurrentSong.Medley.FadeOut_time ;
+    AudioPlayback.SetVolume(vol); //used as fade out!
+  end;
+end;
+
 procedure TScreenSing.Finish;
+var
+  I, J: integer;
+  len, num: integer;
+  Color: TRGB;
 begin
   AudioInput.CaptureStop;
   AudioPlayback.Stop;
@@ -879,6 +1088,91 @@ begin
   end;
 
   SetFontItalic(false);
+
+  if ScreenSong.Mode = smMedley then
+  begin
+    {***** just a quick and dirty fix.... *******}
+    // setup score manager
+    Scores.ClearPlayers; // clear old player values
+
+    Color.R := 0;
+    Color.G := 0;
+    Color.B := 0;
+    // add new players
+    for I := 0 to PlayersPlay - 1 do
+    begin
+      Scores.AddPlayer(Tex_ScoreBG[I], Color);
+    end;
+
+    Scores.Init; // get positions for players
+
+    // prepare players
+    SetLength(Player, PlayersPlay);
+    {***** end of quick and dirty fix ******}
+
+    if not FadeOut then
+    begin
+      inc(PlaylistMedley.CurrentMedleySong);
+      if PlaylistMedley.CurrentMedleySong<=PlaylistMedley.NumMedleySongs then
+      begin
+        //AudioPlayback.PlaySound(SoundLib.Applause);
+        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
+
+        FadeOut:=true;
+        FadeTo(@ScreenScore);
+      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;
+  end;
 end;
 
 procedure TScreenSing.OnSentenceEnd(SentenceIndex: cardinal);
@@ -904,10 +1198,19 @@ begin
     Exit;
 
   // set max song score
-  if (Ini.LineBonus = 0) then
-    MaxSongScore := MAX_SONG_SCORE
-  else
-    MaxSongScore := MAX_SONG_SCORE - MAX_SONG_LINE_BONUS;
+  if ScreenSong.Mode <> smMedley then
+  begin
+    if (Ini.LineBonus = 0) then
+      MaxSongScore := MAX_SONG_SCORE
+    else
+      MaxSongScore := MAX_SONG_SCORE - MAX_SONG_LINE_BONUS;
+  end else
+  begin
+    if (Ini.LineBonus = 0) then
+      MaxSongScore := max_song_score_medley
+    else
+      MaxSongScore := max_song_score_medley - max_song_line_bonus_medley;
+  end;
 
   // Note: ScoreValue is the sum of all note values of the song
   MaxLineScore := MaxSongScore * (Line.TotalNotes / Lines[0].ScoreValue);
@@ -941,7 +1244,11 @@ begin
     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) -
+      if ScreenSong.Mode <> smMedley then
+        LineBonus := MAX_SONG_LINE_BONUS / (Length(Lines[0].Line) -
+          NumEmptySentences)
+      else
+        LineBonus := max_song_line_bonus_medley / (Length(Lines[0].Line) -
         NumEmptySentences);
       // apply line-bonus
       CurrentPlayer.ScoreLine :=
diff --git a/Medley/src/screens/UScreenSong.pas b/Medley/src/screens/UScreenSong.pas
index bd5eebe5..bab2ef51 100644
--- a/Medley/src/screens/UScreenSong.pas
+++ b/Medley/src/screens/UScreenSong.pas
@@ -53,6 +53,8 @@ uses
   UTime;
 
 type
+  TVisArr = array of integer;
+
   TScreenSong = class(TMenu)
     private
       Equalizer: Tms_Equalizer;
@@ -138,6 +140,9 @@ type
       procedure HideCatTL;// Show Cat in Tob left
       procedure Refresh; //Refresh Song Sorting
       procedure ChangeMusic;
+
+      function  getVisibleMedleyArr(): TVisArr;
+      procedure StartMedley(num: integer);
       //Party Mode
       procedure SelectRandomSong;
       procedure SetJoker;
@@ -345,6 +350,21 @@ begin
           Exit;
         end;
 
+      Ord('S'):
+        begin
+          if (Length(getVisibleMedleyArr()) > 0) and (Mode = smNormal) and
+            (CatSongs.Song[Interaction].Medley.Source = msTag) then
+          begin
+            StartMedley(0);
+          end;
+        end;
+      Ord('D'):
+        begin
+          if (Length(getVisibleMedleyArr()) > 0) and (Mode = smNormal) then
+          begin
+            StartMedley(5);
+          end;
+        end;
       Ord('M'): //Show SongMenu
         begin
           if (Songs.SongList.Count > 0) then
@@ -1002,6 +1022,11 @@ begin
     // Set texts
     Text[TextArtist].Text := CatSongs.Song[Interaction].Artist;
     Text[TextTitle].Text  :=  CatSongs.Song[Interaction].Title;
+
+    //medley mod
+    if CatSongs.Song[Interaction].Medley.Source = msTag then
+      Text[TextTitle].Text := Text[TextTitle].Text + '[M]';
+
     if (Ini.TabsAtStartup = 1) and (CatSongs.CatNumShow = -1) then
     begin
       Text[TextNumber].Text := IntToStr(CatSongs.Song[Interaction].OrderNum) + '/' + IntToStr(CatSongs.CatCount);
@@ -1496,6 +1521,9 @@ begin
 
   AudioPlayback.Stop;
 
+  if Mode = smMedley then
+    Mode := smNormal;
+
   if Ini.Players <= 3 then PlayersPlay := Ini.Players + 1;
   if Ini.Players  = 4 then PlayersPlay := 6;
 
@@ -1725,19 +1753,29 @@ end;
 
 procedure TScreenSong.StartMusicPreview();
 var
-  Song: TSong;
+  Song:         TSong;
+  success:      boolean;
 begin
   AudioPlayback.Close();
 
+  if CatSongs.Song[Interaction].Main then
+    Exit;
+
   Song := CatSongs.Song[Interaction];
-  if not assigned(Song) then
+  if not assigned(Song) or Song.Main then
     Exit;
 
   if AudioPlayback.Open(Song.Path.Append(Song.Mp3)) then
   begin
     PreviewOpened := Interaction;
-    
-    AudioPlayback.Position := AudioPlayback.Length / 4;
+
+    if Song.Medley.Source <> msNone then
+    begin
+      CurrentSong := Song;
+      AudioPlayback.Position := GetTimeFromBeat(Song.Medley.StartBeat);
+    end else
+      AudioPlayback.Position := AudioPlayback.Length / 4;
+
     // set preview volume
     if (Ini.PreviewFading = 0) then
     begin
@@ -1747,6 +1785,10 @@ begin
     end
     else
     begin
+      AudioPlayback.Position := AudioPlayback.Position - Ini.PreviewFading;
+      if AudioPlayback.Position<0 then
+        AudioPlayback.Position := 0;
+      
       // music fade enabled: start muted and fade-in
       AudioPlayback.SetVolume(0);
       AudioPlayback.FadeIn(Ini.PreviewFading, IPreviewVolumeVals[Ini.PreviewVolume]);
@@ -1796,6 +1838,106 @@ begin
   end;
 end;
 
+function TScreenSong.getVisibleMedleyArr(): TVisArr;
+var
+  I: integer;
+  res: TVisArr;
+begin
+  SetLength(res, 0);
+  for I := 0 to Length(CatSongs.Song) - 1 do
+  begin
+    if CatSongs.Song[I].Visible and (CatSongs.Song[I].Medley.Source = msTag) then
+    begin
+      SetLength(res, Length(res)+1);
+      res[Length(res)-1] := I;
+    end;
+  end;
+  Result := res;
+end;
+
+//start Medley round
+procedure TScreenSong.StartMedley(num: integer);
+  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: integer;
+  var
+    I, num: integer;
+    unused_arr: array of integer;
+    visible_arr: TVisArr;
+  begin
+    SetLength(unused_arr, 0);
+    visible_arr := getVisibleMedleyArr();
+    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
+  StopMusicPreview();
+  Mode := smMedley;
+
+  if num>0 then
+  begin
+    VS := Length(getVisibleMedleyArr());
+    if VS < num then
+      PlaylistMedley.NumMedleySongs := VS
+    else
+    PlaylistMedley.NumMedleySongs := num;
+
+    Randomize;
+    //set up Playlist Medley
+    SetLength(PlaylistMedley.Song, 0);
+    for I := 0 to PlaylistMedley.NumMedleySongs - 1 do
+    begin
+      AddSong(GetNextSongNr);
+    end;
+  end else
+  begin
+    SetLength(PlaylistMedley.Song, 1);
+    PlaylistMedley.Song[0] := Interaction;
+    PlaylistMedley.NumMedleySongs := 1;
+  end;
+  FadeTo(@ScreenSing);
+end;
+
 procedure TScreenSong.SkipTo(Target: cardinal);
 var
   i: integer;
-- 
cgit v1.2.3