aboutsummaryrefslogblamecommitdiffstats
path: root/Game/Code/Screens/UScreenSong.pas
blob: 54dd4563f0e24c95c558ff3d9974704174556e0d (plain) (tree)
1
2
3
4
5
6
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775




                  
                                                                                                                    













                                    
                                      
                                 

           

                                  








                                                                       

                                            














































                                                                                                                   

                                                                                   


























































                                                                                                          
                         






















                                                                   


                                                            

 









                      





                   























                                                             

































                                                                   
































































































































































                                                                                                                                                                                                            























                                             

































































                                                                                           
                                    































































































































































                                                                                                 


                                                                     
                                        

                                                                        






































































































































































































































































































                                                                                                                         





                                                                                                                 







                                                                                                                         





                                                                                                                 







                                                                                                                         












































































                                                                                                                 























                                                        





                                                  

















































































































































                                                                                                                                                                                         

                                       































































































                                                                                                                                             

                                                                                    











                                                                                                       

                                                                                    
                




                                                                                                 










                                                                                                         

                                                                                    








                                                                                                          
                                           




                                         
                                                                                                      




                                                          
                                                                                             





                                                 
                                                                                                       




                                                          
                                                                                              
                




                                                                                                 










                                                                                                         
                                                                                             


























































































































































































































































































































































                                                                                                                                                                                                      


                                        












                                  
                                     






                                                           




                                       
















































































































                                                                                                             

































                                                                                                                     
               



                     










                                             


                                                                                         













                                                                   
                                     




























                                                                                  

                                                                                     




























































































                                                                                                                                            


                                                                                                       















































































































                                                                                           


                                                                                 
                                                


                                       







                                         








































































                                                                                                                 











                                                                                                 





































                                                                                                                                               


































                                                                                                                                           

               

















                                                                                                         
                                            


                      
                                                       





                      









































                                                                                                               



                                         
                                                           













































                                                                                                     
   












                                                       































































































































                                                                                                                                      









                                                           










                                                        
                                                                                                    





                                                       
                                                                                                     










































                                                                          
                                                                                                    





                                                       
                                                                                                     

































                                                                     
                                                                                                 





                                                     
                                                                                                  






























                                                                   
                                                                                                 





                                                     
                                                                                                  




























































                                                                                                                        














































































































































































































































































                                                                                                                          



                                                       












                                                                                            


                                                           














                                                                                         


                                                           













































                                                                                                                                    
                        
          
                                                 
            
                                                                                 

                                      
                                                                                                



                                           










                                                                                      

























































































                                                                        


                                                         




                                                                                                    







                                                 






                                                         




                                                         




























                                                                    
                                                  


                               













                                                 

























                                                      
unit UScreenSong;

interface

uses
  UMenu, TextGL, SDL, UMusic, UDraw, UFiles, UTime, UDisplay, USongs, SysUtils, ULog, UThemes, UTexture, ULanguage,
  ULCD, ULight, UIni, UVideo;

type
  TVisArr = array of integer;

  TVidVis = (none, windowed, full);

  THandler = record
    changed:  boolean;
    change_time:  real;
  end;

  TScreenSong = class(TMenu)
    private
      SkippedSongs: array of integer;
      ChooseableSongs:  integer;

    public
      MP3Volume:        integer;
      MP3VolumeHandler: THandler;
      TextArtist:   integer;
      TextTitle:    integer;
      TextNumber:   integer;

      TextPlugin:   integer;
      TextP1:       integer;  //for M2-MOD: show actual player-name p1
      TextP2:       integer;  //for M2-MOD: show actual player-name p2

      TextMedley:   array[1..4] of integer;
      TextTop:      array[0..2] of integer;
      StaticTop:    integer;

      FoundCAT: boolean;      //for M2-MOD: a cat is chosen, see whats next...

      SongIndex:    integer; //Index of Song that is playing since UScreenScore...

      //Video Icon Mod
      VideoIcon:    Cardinal;

      VidVis:       TVidVis; //video visiability
      StartTry:     boolean;

      MinSource:    TMedleySource;

      MakeMedley:   boolean;

      //VideoAspect
      AspectHandler:  THandler;

      //Medley Icons
      MedleyIcon: cardinal;
      CalcMedleyIcon: cardinal;

      TextCat:   integer;
      StaticCat: integer;

      SongCurrent:  real;
      SongTarget:   real;

      HighSpeed:    boolean;
      CoverFull:    boolean;
      CoverTime:    real;
      CoverX:       integer;
      CoverY:       integer;
      CoverW:       integer;
      is_jump:      boolean; // Jump to Song Mod
      is_jump_title:boolean; //Jump to SOng MOd-YTrue if search for Title

      EqualizerBands: array of Byte;
      EqualizerTime: Cardinal;
      EqualizerTime2: Byte;

      ID:           string; //for help-system

      //Party Mod
      Mode:         TSongMode;  //0 = Standard, 1= Go to PartyMode after Selection + Change to Random Song at Show
                                //2 = M2-Mode, 3=Medley-Mode
      PartyMedley:  boolean;
      PartyPlayed:  array of integer; //played in Party Classic
      MedleyPlayed: array of integer; //played in Challenge or Classic Medley-mode

      SungToEnd: boolean; //Song was sung to the end?

      //party Statics (Joker)
      StaticTeam1Joker1: Cardinal;
      StaticTeam1Joker2: Cardinal;
      StaticTeam1Joker3: Cardinal;
      StaticTeam1Joker4: Cardinal;
      StaticTeam1Joker5: Cardinal;

      StaticTeam2Joker1: Cardinal;
      StaticTeam2Joker2: Cardinal;
      StaticTeam2Joker3: Cardinal;
      StaticTeam2Joker4: Cardinal;
      StaticTeam2Joker5: Cardinal;

      StaticTeam3Joker1: Cardinal;
      StaticTeam3Joker2: Cardinal;
      StaticTeam3Joker3: Cardinal;
      StaticTeam3Joker4: Cardinal;
      StaticTeam3Joker5: Cardinal;

      TextNumJokerTeam1: Cardinal;
      TextNumJokerTeam2: Cardinal;
      TextNumJokerTeam3: Cardinal;

      StaticParty:    Array of Cardinal;
      TextParty:      Array of Cardinal;
      StaticNonParty: Array of Cardinal;
      TextNonParty:   Array of Cardinal;
      StaticM2Party: Array of Cardinal;
      TextM2Party:   Array of Cardinal;


      constructor Create; override;
      procedure SetScroll;
      procedure SetScroll1;
      procedure SetScroll2;
      procedure SetScroll3;
      procedure SetScroll4;
      procedure SetScroll5;
      function ParseInput(PressedKey: Cardinal; ScanCode: byte; PressedDown: Boolean): Boolean; override;
      function Draw: boolean; override;
      procedure onShow; override;
      procedure onHide; override;
      procedure SelectNext;
      procedure SelectPrev;
      procedure UpdateLCD;
      procedure SkipTo(Target: Cardinal);
      procedure RandomSongChallenge();
      procedure SkipTo2(Target: Cardinal); //skipt exactly to the target song nr.
      procedure FixSelected; //Show Wrong Song when Tabs on Fix
      procedure FixSelected2; //Show Wrong Song when Tabs on Fix
      procedure ShowCatTL(Cat: Integer);// Show Cat in Top left
      procedure ShowCatTLCustom(Caption: String);// Show Custom Text in Top left
      procedure HideCatTL;// Show Cat in Tob left
      procedure Refresh; //Refresh Song Sorting
      procedure DrawEqualizer;
      procedure ChangeMusic;
      procedure LoadTop;
      procedure StartVideoPreview;
      //Party Mode
      procedure SelectRandomSong;
      procedure SetJoker;
      procedure SetStatics;
      //procedures for Menu
      procedure StartSong;
      procedure OpenEditor;
      procedure DoJoker(Team: Byte; SDL_ModState: Word);
      procedure SelectPlayers;

      procedure UnLoadDetailedCover;

      //Extensions
      procedure DrawExtensions;

      //for M2-MOD:
      function SongSkipped(SongNr: integer): boolean;
      function GetSongsSkipped(): Integer;
      procedure DoJokerM2;
      function  getVisibleMedleyArr(MinS: TMedleySource): TVisArr;
      procedure StartMedley(num: integer; MinS: TMedleySource);
      procedure DrawAspect;

      function PartyPlayedSong(SongNr: integer): boolean;
      function PartyPlayedMedley(SongNr: integer): boolean;


  end;

implementation
uses UGraphic, UMain,
  UCovers,
  math,
  gl,
  Windows,
  USkins,
  UHelp,
  UDLLManager,
  UDataBase,
  UParty,
  UPartyM2,
  UPlaylist,
  UScreenSongMenu;

// ***** Public methods ****** //
function  TScreenSong.SongSkipped(SongNr: integer): boolean;
var
  i: integer;
  skipped :boolean;
begin
  skipped := false;
  for i := 0 to Length(SkippedSongs) - 1 do
  begin
    if (SongNr=SkippedSongs[i]) then
    begin
      skipped:=true;
      break;
    end;
  end;
  Result:=skipped;
end;

function TScreenSong.GetSongsSkipped(): Integer;
begin
  Result := Length(SkippedSongs);
end;

function  TScreenSong.PartyPlayedSong(SongNr: integer): boolean;
var
  i: integer;
  played :boolean;
begin
  played := false;
  for i := 0 to Length(PartyPlayed) - 1 do
  begin
    if (SongNr=PartyPlayed[i]) then
    begin
      played:=true;
      break;
    end;
  end;
  Result:=played;
end;

function  TScreenSong.PartyPlayedMedley(SongNr: integer): boolean;
var
  i: integer;
  played :boolean;
begin
  played := false;
  for i := 0 to Length(MedleyPlayed) - 1 do
  begin
    if (SongNr=MedleyPlayed[i]) then
    begin
      played:=true;
      break;
    end;
  end;
  Result:=played;
end;

//Show Wrong Song when Tabs on Fix
procedure TScreenSong.FixSelected;
var I, I2: Integer;
  begin
    if CatSongs.VisibleSongs > 0 then
    begin
      I2:= 0;
      for I := low(CatSongs.Song) to High(Catsongs.Song) do
      begin
        if CatSongs.Song[I].Visible then
          inc(I2);

        if I = Interaction - 1 then
          break;
      end;

      SongCurrent := I2;
      SongTarget  := I2;
    end;
  end;

procedure TScreenSong.FixSelected2;
var I, I2: Integer;
  begin
    if CatSongs.VisibleSongs > 0 then
    begin
      I2:= 0;
      for I := low(CatSongs.Song) to High(Catsongs.Song) do
      begin
        if CatSongs.Song[I].Visible then
          inc(I2);

        if I = Interaction - 1 then
          break;
      end;

      SongTarget  := I2;
    end;
  end;
//Show Wrong Song when Tabs on Fix End

  procedure TScreenSong.ShowCatTLCustom(Caption: String);// Show Custom Text in Top left
  begin
    Text[TextCat].Text := Caption;
    Text[TextCat].Visible := true;
    Static[StaticCat].Visible := False;
  end;

  //Show Cat in Top Left Mod
  procedure TScreenSong.ShowCatTL(Cat: Integer);
    begin
    //Change
    Text[TextCat].Text := CatSongs.Song[Cat].Artist;
    //showmessage(CatSongs.Song[Cat].Path + CatSongs.Song[Cat].Cover);
    //Static[StaticCat].Texture := Texture.GetTexture(Button[Cat].Texture.Name, 'Plain', true);

    Static[StaticCat].Texture := Texture.GetTexture(Button[Cat].Texture.Name, 'Plain', true);
    //Texture.GetTexture(Button[Cat].Texture.Name, 'Plain', false);
    //Button[Cat].
    //Cover


    //Show
    Text[TextCat].Visible := true;
    Static[StaticCat].Visible := True;
    end;

  procedure TScreenSong.HideCatTL;
    begin
    //Hide
    //Text[TextCat].Visible := false;
    Static[StaticCat].Visible := false;
    //New -> Show Text specified in Theme
    Text[TextCat].Visible := True;
    Text[TextCat].Text := Theme.Song.TextCat.Text;
    end;
    //Show Cat in Top Left Mod End


// Method for input parsing. If False is returned, GetNextWindow
// should be checked to know the next window to load;
function TScreenSong.ParseInput(PressedKey: Cardinal; ScanCode: byte; PressedDown: Boolean): Boolean;
var
  I:      integer;
  I2:     integer;
  HS:     integer;
  SDL_ModState:  Word;
  Letter: Char;
  VisArr: array of integer;
begin
  Result := true;

  //Song Screen Extensions (Jumpto + Menu)
  if (ScreenSongMenu.Visible) then
  begin
    Result := ScreenSongMenu.ParseInput(PressedKey, ScanCode, PressedDown);
    Exit;
  end
  else if (ScreenSongJumpto.Visible) then
  begin
    Result := ScreenSongJumpto.ParseInput(PressedKey, ScanCode, PressedDown);
    Exit;
  end;

  If (PressedDown) Then
  begin // Key Down

    SDL_ModState := SDL_GetModState and (KMOD_LSHIFT + KMOD_RSHIFT
    + KMOD_LCTRL + KMOD_RCTRL + KMOD_LALT  + KMOD_RALT);

    //Jump to Artist/Titel  (not in party-mode)
    if (SDL_ModState and KMOD_LALT <> 0) AND (Mode = smNormal) AND (PressedKey >= SDLK_A) AND (PressedKey <= SDLK_Z) then
    begin
      Letter := UpCase(Chr(ScanCode));
      I2 := Length(CatSongs.Song);

      //Jump To Titel
      if (SDL_ModState = KMOD_LALT or KMOD_LSHIFT) then
      begin
        For I := 1 to high(CatSongs.Song) do
        begin
          if (CatSongs.Song[(I + Interaction) mod I2].Visible) AND
            (Length(CatSongs.Song[(I + Interaction) mod I2].Title)>0) AND
            (UpCase(CatSongs.Song[(I + Interaction) mod I2].Title[1]) = Letter) then
          begin
            SkipTo(CatSongs.VisibleIndex((I + Interaction) mod I2));

            Music.PlayChange;

            ChangeMusic;
            SetScroll4;
            UpdateLCD;
            //Break and Exit
            Exit;
          end;
        end;
      end
      //Jump to Artist
      else  if (SDL_ModState = KMOD_LALT) then
      begin
        For I := 1 to high(CatSongs.Song) do
        begin
          if (CatSongs.Song[(I + Interaction) mod I2].Visible) AND (Length(CatSongs.Song[(I + Interaction) mod I2].Artist)>0) AND (UpCase(CatSongs.Song[(I + Interaction) mod I2].Artist[1]) = Letter) then
          begin
            SkipTo(CatSongs.VisibleIndex((I + Interaction) mod I2));

            Music.PlayChange;

            ChangeMusic;
            SetScroll4;
            UpdateLCD;

            //Break and Exit
            Exit;
          end;
        end;
      end;
      Exit;
    end;

    case PressedKey of
      //MP3-Volume Up
      SDLK_PAGEUP:
        begin
          if (MP3Volume<100) then
          begin
            MP3Volume := MP3Volume+5;
            Music.SetMusicVolume(MP3Volume);
          end;
          MP3VolumeHandler.changed := true;
          MP3VolumeHandler.change_time := 0;
        end;

      //MP3-Volume Down
      SDLK_PAGEDOWN:
        begin
          if (MP3Volume>0) then
          begin
            MP3Volume := MP3Volume-5;
            Music.SetMusicVolume(MP3Volume);
          end;
          MP3VolumeHandler.changed := true;
          MP3VolumeHandler.change_time := 0;
        end;

      SDLK_K:
        begin
          if Music.VocalRemoverActivated() then
            Music.DisableVocalRemover
          else
            Music.EnableVocalRemover;
        end;
        
      SDLK_A:
        begin
          if VidVis = full then

            ToggleAspectCorrection;

            DataBase.SetAspect(CatSongs.Song[Interaction].Artist,
              CatSongs.Song[Interaction].Title, integer(UVideo.fAspectCorrection));

            AspectHandler.changed := true;
            AspectHandler.change_time := Czas.Teraz;
        end;
      SDLK_V:
        begin
          if UVideo.VideoOpened then
          begin
            if VidVis=full then
              VidVis:=windowed
            else begin
              VidVis:=full;
              UVideo.SetAspectCorrection(TAspectCorrection(
                DataBase.GetAspect(CatSongs.Song[Interaction].Artist,
                CatSongs.Song[Interaction].Title, Ini.AspectCorrect)));
              AspectHandler.changed := true;
              AspectHandler.change_time := Czas.Teraz;
            end;

          end;
        end;

      SDLK_TAB:
        begin
          Help.SetHelpID(ID);
          ScreenPopupHelp.ShowPopup();
        end;

      SDLK_Q:
        begin
          Result := false;
        end;

      SDLK_S:
        begin
          if (SDL_ModState = KMOD_LSHIFT) and not MakeMedley and
            (CatSongs.Song[Interaction].Medley.Source>=msCalculated) and
            (Mode = smNormal)then
            StartMedley(0, msCalculated)
          else if (CatSongs.Song[Interaction].Medley.Source>=msTag) and not MakeMedley and
            (Mode = smNormal) then
            StartMedley(0, msTag);
        end;

      SDLK_D:
        begin
          if (Mode = smNormal) and (SDL_ModState = KMOD_LSHIFT) and not MakeMedley and
            (length(getVisibleMedleyArr(msCalculated))>0) then
            StartMedley(5, msCalculated)
          else if (Mode = smNormal) and (Length(getVisibleMedleyArr(msTag)) > 0)
            and not MakeMedley then
            StartMedley(5, msTag);
        end;

      SDLK_F:
        begin
          if (Mode = smNormal) and (SDL_ModState = KMOD_LSHIFT) and MakeMedley then
          begin
            if Length(PlaylistMedley.Song)>0 then
            begin
              SetLength(PlaylistMedley.Song, Length(PlaylistMedley.Song)-1);
              PlaylistMedley.NumMedleySongs := Length(PlaylistMedley.Song);
            end;

            if Length(PlaylistMedley.Song)=0 then
              MakeMedley := false;
          end else if (Mode = smNormal) and (Length(getVisibleMedleyArr(msCalculated)) > 0) then
          begin
            MakeMedley := true;
            StartMedley(99, msCalculated);
          end;
        end;

      SDLK_ESCAPE,
      SDLK_BACKSPACE :
        begin
        if (Mode = smNormal) or ((Mode = smChallenge) and not PartyMedley and not FoundCAT) then
        begin
          //On Escape goto Cat-List Hack
          if (Ini.Tabs_at_startup = 1) AND (CatSongs.CatNumShow <> -1) then
            begin
            //Find Category
            I := Interaction;
            while not catsongs.Song[I].Main  do
              begin
              Dec (I);
              if (I < low(catsongs.Song)) then
                break;
              end;
            if (I<= 1) then
            Interaction := high(catsongs.Song)
            else
            Interaction := I - 1;

            //Stop Music
            Music.Stop;

            //Stop Video
            acClose;
            VidVis := none;
            CatSongs.ShowCategoryList;

            //Show Cat in Top Left Mod
            HideCatTL;


            //Show Wrong Song when Tabs on Fix
            SelectNext;
            FixSelected;
            //SelectPrev;
            //CatSongs.Song[0].Visible := False;
            end
          else
          begin
          //On Escape goto Cat-List Hack End
            //Tabs off and in Search or Playlist -> Go back to Song view
            if (CatSongs.CatNumShow < -1) then
            begin
              //Atm: Set Empty Filter
              CatSongs.SetFilter('', 0);

              //Show Cat in Top Left Mod
              HideCatTL;
              Interaction := 0;

              //Show Wrong Song when Tabs on Fix
              SelectNext;
              FixSelected;

              ChangeMusic;
            end
            else if (Mode = smNormal) then
            begin
              Music.Stop;
              Music.PlayBack;
              acClose;
              VidVis := none;
              FadeTo(@ScreenMain);
            end;

          end;
        end
        //When in party Mode then Ask before Close
        else if (Mode = smParty) then
        begin
          Music.PlayBack;
          CheckFadeTo(@ScreenMain,'MSG_END_PARTY');
        end;
        end;
      SDLK_RETURN:
        begin
          if Length(Songs.Song) > 0 then
          begin
//            PortWriteB($378, 0);
            if CatSongs.Song[Interaction].Main then
            begin // clicked on Category Button

              //Show Cat in Top Left Mod
              ShowCatTL (Interaction);

              //I := CatSongs.VisibleIndex(Interaction);
              CatSongs.ClickCategoryButton(Interaction);
              {I2 := CatSongs.VisibleIndex(Interaction);
              SongCurrent := SongCurrent - I + I2;
              SongTarget := SongTarget - I + I2; }

//              if I<>I2 then beep;
              //  SetScroll4;

              //Show Wrong Song when Tabs on Fix
              SelectNext;
              FixSelected;

              //Play Music:
              ChangeMusic;

            end else
            begin // clicked on song
              if (Mode = smNormal) then //Normal Mode -> Start Song
              begin
                if MakeMedley then
                begin
                  Mode := smMedley;
                  Music.Stop;
                  //Do the Action that is specified in Ini
                  case Ini.OnSongClick of
                    0: FadeTo(@ScreenSing);
                    1: SelectPlayers;
                    2: FadeTo(@ScreenSing);
                  end;
                end else
                begin
                  //Do the Action that is specified in Ini
                  case Ini.OnSongClick of
                    0: StartSong;
                    1: SelectPlayers;
                    2:begin
                      VidVis := windowed;
                      If (CatSongs.CatNumShow = -3) then
                        ScreenSongMenu.MenuShow(SM_Playlist)
                      else
                        ScreenSongMenu.MenuShow(SM_Main);
                    end;
                  end;
                end;
              end
              else if (Mode = smParty) and not PartyMedley then //PartyMode classic -> Show Menu
              begin
                VidVis := windowed;
                if (Ini.PartyPopup = 1) then
                  ScreenSongMenu.MenuShow(SM_Party_Main)
                else begin
                  SetLength(PartyPlayed, Length(PartyPlayed)+1);
                  PartyPlayed[Length(PartyPlayed)-1] := Interaction;
                  ScreenSong.StartSong;
                end;
              end else if (Mode = smChallenge) and not PartyMedley then
              begin
                PartySessionM2.AddSongPlayed(CatSongs.CatNumShow, Interaction);
                ScreenSong.StartSong;
              end else if PartyMedley then
              begin
                StartMedley(5, MinSource);
              end;
            end;
          end;
        end;

      SDLK_M: //Show SongMenu
        begin
          if (Length(Songs.Song) > 0) and (Mode <> smChallenge) then begin //not in M2-Mode
            if (Mode = smNormal) then begin
              if not CatSongs.Song[Interaction].Main then begin // clicked on Song
                if CatSongs.CatNumShow = -3 then
                  ScreenSongMenu.MenuShow(SM_Playlist)
                else
                  ScreenSongMenu.MenuShow(SM_Main);
              end
              else
              begin
                ScreenSongMenu.MenuShow(SM_Playlist_Load);
              end;
            end //Party Mode -> Show Party Menu
            else ScreenSongMenu.MenuShow(SM_Party_Main);
          end;
        end;

      SDLK_P: //Show Playlist Menu
        begin
          if (Length(Songs.Song) > 0) AND (Mode = smNormal) then begin //not in party-modes
              ScreenSongMenu.MenuShow(SM_Playlist_Load);
          end;
        end;

      SDLK_J: //Show Jumpto Menu / Joker
        begin
          if (Length(Songs.Song) > 0) AND (Mode = smNormal) then //not in party-modes
          begin
            VidVis := windowed;
            ScreenSongJumpto.Visible := True;
          end else if (Mode=smChallenge) and not PartyMedley then  //M2-MOD-mode
            DoJokerM2
          else if PartyMedley and (Mode=smChallenge) then
          begin
            DoJoker(0, SDL_ModState)
          end;
        end;

      SDLK_DOWN:
        begin
          if (Mode = smNormal) or ((Mode = smChallenge) and not FoundCAT) then
          begin
            //Only Change Cat when not in Playlist or Search Mode
            if (CatSongs.CatNumShow > -2) then
            begin
              //Cat Change Hack
              if Ini.Tabs_at_startup = 1 then
              begin
                I := Interaction;
                if I <= 0 then I := 1;

                while not catsongs.Song[I].Main do
                begin
                  Inc (I);
                  if (I > high(catsongs.Song)) then
                    I := low(catsongs.Song);
                end;

                Interaction := I;

                //Show Cat in Top Left Mod
                ShowCatTL (Interaction);

                CatSongs.ClickCategoryButton(Interaction);
                SelectNext;
                FixSelected;

                //Play Music:
                Music.PlayChange;
                ChangeMusic;

              end;

            //
            //Cat Change Hack End}
            end;
          end;
        end;
      SDLK_UP:
        begin
          if (Mode = smNormal) or ((Mode = smChallenge) and not FoundCAT) then
          begin
            //Only Change Cat when not in Playlist or Search Mode
            if (CatSongs.CatNumShow > -2) then
            begin
              //Cat Change Hack
              if Ini.Tabs_at_startup = 1 then
              begin
                I := Interaction;
                I2 := 0;
                if I <= 0 then I := 1;

                while not catsongs.Song[I].Main or (I2 = 0) do
                begin
                  if catsongs.Song[I].Main then
                    Inc(I2);
                  Dec (I);
                  if (I < low(catsongs.Song)) then
                    I := high(catsongs.Song);
                end;

                Interaction := I;

                //Show Cat in Top Left Mod
                ShowCatTL (I);

                CatSongs.ClickCategoryButton(I);
                SelectNext;
                FixSelected;

                //Play Music:
                Music.PlayChange;
                ChangeMusic;
              end;
            end;
            //Cat Change Hack End}
          end;
        end;

      SDLK_RIGHT:
        begin
          if (Length(Songs.Song) > 0) AND
            ((Mode = smNormal) or ((Mode = smChallenge) and CatSongs.Song[Interaction].Main)) then
          begin
            Music.PlayChange;
            SelectNext;
            ChangeMusic;
            SetScroll4;
            UpdateLCD;
            Light.LightOne(1, 200);
          end;
        end;

      SDLK_LEFT:
        begin
          if (Length(Songs.Song) > 0) AND
            ((Mode = smNormal) or ((Mode = smChallenge) and CatSongs.Song[Interaction].Main)) then
          begin
            Music.PlayChange;
            SelectPrev;
            ChangeMusic;
            SetScroll4;
            UpdateLCD;
            Light.LightOne(0, 200);
          end;
        end;

      SDLK_E:
        begin
          if (Mode = smNormal) then
            OpenEditor;
        end;
        
      SDLK_R:
        begin
          if (Length(Songs.Song) > 0) and (Mode = smNormal) then
          begin
            if (SDL_ModState = KMOD_LSHIFT) AND (Ini.Tabs_at_startup = 1) then
            //Random Category
            begin
              SetLength(VisArr, 0);

              //Search Cat
              for I2 := Interaction downto low(CatSongs.Song) do
              begin
                if CatSongs.Song[I2].Main then
                  break;
              end;

              for I := 0 to Length(CatSongs.Song) - 1 do
              begin
                if CatSongs.Song[I].Main and not (I=I2) then
                begin
                  SetLength(VisArr, Length(VisArr)+1);
                  VisArr[Length(VisArr)-1] := I;
                end;
              end;

              if (Length(VisArr)>0) then
              begin
                I2 := Random(Length(VisArr));
                I2 := VisArr[I2];

                //Search Cat
                for I := I2 downto low(CatSongs.Song) do
                begin
                  if CatSongs.Song[I].Main then
                    break;
                end;
                //In I ist jetzt die Kategorie in I2 der Song
                //I is the CatNum, I2 is the No of the Song within this Cat

                //Choose Cat
                CatSongs.ShowCategoryList;

                //Show Cat in Top Left Mod
                ShowCatTL (I);

                //CatSongs.ClickCategoryButton(I);
                //SelectNext;

                //Choose Song
                SkipTo2(I);
              end;
            end else if (SDL_ModState = KMOD_LCTRL) AND (Ini.Tabs_at_startup = 1) then
            //random in All Categorys
            begin
              SetLength(VisArr, 0);

              for I := 0 to Length(CatSongs.Song) - 1 do
              begin
                if not CatSongs.Song[I].Main and (I<>Interaction)then
                begin
                  SetLength(VisArr, Length(VisArr)+1);
                  VisArr[Length(VisArr)-1] := I;
                end;
              end;


              if (Length(VisArr)>0) then
              begin
                I2 := Random(Length(VisArr));
                I2 := VisArr[I2];

                //Search Cat
                for I := I2 downto low(CatSongs.Song) do
                begin
                  if CatSongs.Song[I].Main then
                    break;
                end;

                //Choose Cat
                CatSongs.ShowCategoryList;

                //Show Cat in Top Left Mod
                ShowCatTL (I);

                CatSongs.ClickCategoryButton(I);
                //SelectNext;

                //Choose Song
                SkipTo2(I2);
              end;
            end else //Random in one Category
            begin
              SetLength(VisArr, 0);

              for I := 0 to Length(CatSongs.Song) - 1 do
              begin
                if CatSongs.Song[I].Visible and not (I=Interaction)then
                begin
                  SetLength(VisArr, Length(VisArr)+1);
                  VisArr[Length(VisArr)-1] := I;
                end;
              end;


              if (Length(VisArr)>0) then
              begin
                I := Random(Length(VisArr));
                I := VisArr[I];

                //Choose Song
                SkipTo2(I);
              end;
              //old: SkipTo(Random(CatSongs.VisibleSongs));
            end;
            Music.PlayChange;
            ChangeMusic;
            SetScroll4;
          end else if (Mode = smChallenge) and not PartyMedley then //M2-MOD-mode
            DoJokerM2
          else if (Mode = smChallenge) and PartyMedley then
            DoJoker(0, SDL_ModState);
        end;

      SDLK_1:
        begin //Joker Team 1
          if (Mode = smParty) AND (PartySession.Teams.NumTeams >= 1) AND (PartySession.Teams.Teaminfo[0].Joker > 0) then
          begin
            //Joker spielen
            DoJoker(0, SDL_ModState);
          end else if (Mode = smNormal) and (SDL_ModState = KMOD_LSHIFT or KMOD_LCTRL) and not MakeMedley and
            (length(getVisibleMedleyArr(msCalculated))>0) then
            StartMedley(10, msCalculated)
          else if (Mode = smNormal) and (SDL_ModState = KMOD_LCTRL) and (Length(getVisibleMedleyArr(msTag)) > 0)
            and not MakeMedley then
            StartMedley(10, msTag);
        end;

      SDLK_2:
        begin //Joker Team 2
          if (Mode = smParty) AND (PartySession.Teams.NumTeams >= 2) AND (PartySession.Teams.Teaminfo[1].Joker > 0) then
          begin
            //Joker spielen
            DoJoker(1, SDL_ModState);
          end else if (Mode = smNormal) and (SDL_ModState = KMOD_LSHIFT or KMOD_LCTRL) and not MakeMedley and
            (length(getVisibleMedleyArr(msCalculated))>0) then
            StartMedley(20, msCalculated)
          else if (Mode = smNormal) and (SDL_ModState = KMOD_LCTRL) and (Length(getVisibleMedleyArr(msTag)) > 0)
            and not MakeMedley then
            StartMedley(20, msTag);
        end;

      SDLK_3:
        begin //Joker Team 3
          if (Mode = smParty) AND (PartySession.Teams.NumTeams >= 3) AND (PartySession.Teams.Teaminfo[2].Joker > 0) then
          begin
            //Joker spielen
            DoJoker(2, SDL_ModState);
          end else if (Mode = smNormal) and (SDL_ModState = KMOD_LSHIFT or KMOD_LCTRL) and not MakeMedley and
            (length(getVisibleMedleyArr(msCalculated))>0) then
            StartMedley(30, msCalculated)
          else if (Mode = smNormal) and (SDL_ModState = KMOD_LCTRL) and (Length(getVisibleMedleyArr(msTag)) > 0)
            and not MakeMedley then
            StartMedley(30, msTag);
        end;

      SDLK_4:
        begin
          if (Mode = smNormal) and (SDL_ModState = KMOD_LSHIFT or KMOD_LCTRL) and not MakeMedley and
            (length(getVisibleMedleyArr(msCalculated))>0) then
            StartMedley(40, msCalculated)
          else if (Mode = smNormal) and (SDL_ModState = KMOD_LCTRL) and (Length(getVisibleMedleyArr(msTag)) > 0)
            and not MakeMedley then
            StartMedley(40, msTag);
        end;

      SDLK_5:
        begin
          if (Mode = smNormal) and (SDL_ModState = KMOD_LSHIFT or KMOD_LCTRL) and not MakeMedley and
            (length(getVisibleMedleyArr(msCalculated))>0) then
            StartMedley(50, msCalculated)
          else if (Mode = smNormal) and (SDL_ModState = KMOD_LCTRL) and (Length(getVisibleMedleyArr(msTag)) > 0)
            and not MakeMedley then
            StartMedley(50, msTag);
        end;

      SDLK_6:
        begin
          if (Mode = smNormal) and (SDL_ModState = KMOD_LSHIFT or KMOD_LCTRL) and not MakeMedley and
            (length(getVisibleMedleyArr(msCalculated))>0) then
            StartMedley(60, msCalculated)
          else if (Mode = smNormal) and (SDL_ModState = KMOD_LCTRL) and (Length(getVisibleMedleyArr(msTag)) > 0)
            and not MakeMedley then
            StartMedley(60, msTag);
        end;

      SDLK_7:
        begin
          if (Mode = smNormal) and (SDL_ModState = KMOD_LSHIFT or KMOD_LCTRL) and not MakeMedley and
            (length(getVisibleMedleyArr(msCalculated))>0) then
            StartMedley(70, msCalculated)
          else if (Mode = smNormal) and (SDL_ModState = KMOD_LCTRL) and (Length(getVisibleMedleyArr(msTag)) > 0)
            and not MakeMedley then
            StartMedley(70, msTag);
        end;

      SDLK_8:
        begin
          if (Mode = smNormal) and (SDL_ModState = KMOD_LSHIFT or KMOD_LCTRL) and not MakeMedley and
            (length(getVisibleMedleyArr(msCalculated))>0) then
            StartMedley(80, msCalculated)
          else if (Mode = smNormal) and (SDL_ModState = KMOD_LCTRL) and (Length(getVisibleMedleyArr(msTag)) > 0)
            and not MakeMedley then
            StartMedley(80, msTag);
        end;

      SDLK_9:
        begin
          if (Mode = smNormal) and (SDL_ModState = KMOD_LSHIFT or KMOD_LCTRL) and not MakeMedley and
            (length(getVisibleMedleyArr(msCalculated))>0) then
            StartMedley(90, msCalculated)
          else if (Mode = smNormal) and (SDL_ModState = KMOD_LCTRL) and (Length(getVisibleMedleyArr(msTag)) > 0)
            and not MakeMedley then
            StartMedley(90, msTag);
        end;

      //stress test :>
      SDLK_0:
        begin
          if (Mode = smNormal) and (SDL_ModState = KMOD_LSHIFT or KMOD_LCTRL) and not MakeMedley and
            (length(getVisibleMedleyArr(msCalculated))>0) then
            StartMedley(10000, msCalculated)
          else if (Mode = smNormal) and (SDL_ModState = KMOD_LCTRL) and (Length(getVisibleMedleyArr(msTag)) > 0)
            and not MakeMedley then
            StartMedley(10000, msTag);
        end;
    end;
  end;
end;

constructor TScreenSong.Create;
var
  Pet:    integer;
  I:      integer;
Label CreateSongButtons;
begin
  inherited Create;

  LoadFromTheme(Theme.Song);

  TextArtist := AddText(Theme.Song.TextArtist);
  TextTitle :=  AddText(Theme.Song.TextTitle);
  TextNumber := AddText(Theme.Song.TextNumber);

  TextPlugin := AddText(Theme.Song.TextPlugin);

  for I := 1 to 4 do
    TextMedley[I] := AddText(Theme.Song.TextMedley[I]);

  for I := 0 to 2 do
    TextTop[I] := AddText(Theme.Song.TextTop[I]);

  StaticTop := AddStatic(Theme.Song.StaticTop);
  Static[StaticTop].Texture.Alpha := 0.5;

  //for M2-MOD-mode:
  TextP1 := AddText(Theme.Song.TextP1);
  TextP2 := AddText(Theme.Song.TextP2);

  //Show Cat in Top Left mod
  TextCat := AddText(Theme.Song.TextCat);
  StaticCat :=  AddStatic(Theme.Song.StaticCat);

  //Show Video Icon Mod
  VideoIcon := AddStatic(Theme.Song.VideoIcon);

  //Meldey Icons
  MedleyIcon := AddStatic(Theme.Song.MedleyIcon);
  CalcMedleyIcon := AddStatic(Theme.Song.CalculatedMedleyIcon);

  //Party Mode
  StaticTeam1Joker1 := AddStatic(Theme.Song.StaticTeam1Joker1);
  StaticTeam1Joker2 := AddStatic(Theme.Song.StaticTeam1Joker2);
  StaticTeam1Joker3 := AddStatic(Theme.Song.StaticTeam1Joker3);
  StaticTeam1Joker4 := AddStatic(Theme.Song.StaticTeam1Joker4);
  StaticTeam1Joker5 := AddStatic(Theme.Song.StaticTeam1Joker5);

  StaticTeam2Joker1 := AddStatic(Theme.Song.StaticTeam2Joker1);
  StaticTeam2Joker2 := AddStatic(Theme.Song.StaticTeam2Joker2);
  StaticTeam2Joker3 := AddStatic(Theme.Song.StaticTeam2Joker3);
  StaticTeam2Joker4 := AddStatic(Theme.Song.StaticTeam2Joker4);
  StaticTeam2Joker5 := AddStatic(Theme.Song.StaticTeam2Joker5);

  StaticTeam3Joker1 := AddStatic(Theme.Song.StaticTeam3Joker1);
  StaticTeam3Joker2 := AddStatic(Theme.Song.StaticTeam3Joker2);
  StaticTeam3Joker3 := AddStatic(Theme.Song.StaticTeam3Joker3);
  StaticTeam3Joker4 := AddStatic(Theme.Song.StaticTeam3Joker4);
  StaticTeam3Joker5 := AddStatic(Theme.Song.StaticTeam3Joker5);

  TextNumJokerTeam1 := AddText(Theme.Song.SongTextPartyTeam1NumJoker);
  TextNumJokerTeam2 := AddText(Theme.Song.SongTextPartyTeam2NumJoker);
  TextNumJokerTeam3 := AddText(Theme.Song.SongTextPartyTeam3NumJoker);

  //Load Party and NonParty specific Statics and Texts
  SetLength(StaticParty, Length(Theme.Song.StaticParty));
  for I := 0 to High(Theme.Song.StaticParty) do
    StaticParty[I] := AddStatic(Theme.Song.StaticParty[I]);

  SetLength(TextParty, Length(Theme.Song.TextParty));
  for I := 0 to High(Theme.Song.TextParty) do
    TextParty[I] := AddText(Theme.Song.TextParty[I]);

  SetLength(StaticNonParty, Length(Theme.Song.StaticNonParty));
  for I := 0 to High(Theme.Song.StaticNonParty) do
    StaticNonParty[I] := AddStatic(Theme.Song.StaticNonParty[I]);

  SetLength(TextNonParty, Length(Theme.Song.TextNonParty));
  for I := 0 to High(Theme.Song.TextNonParty) do
    TextNonParty[I] := AddText(Theme.Song.TextNonParty[I]);

  SetLength(StaticM2Party, Length(Theme.Song.StaticM2Party));
  for I := 0 to High(Theme.Song.StaticM2Party) do
    StaticM2Party[I] := AddStatic(Theme.Song.StaticM2Party[I]);

  SetLength(TextM2Party, Length(Theme.Song.TextM2Party));
  for I := 0 to High(Theme.Song.TextM2Party) do
    TextM2Party[I] := AddText(Theme.Song.TextM2Party[I]);

  // Song List
//  Songs.LoadSongList; // moved to the UltraStar unit
  CatSongs.Refresh;

  if (length(CatSongs.Song) > 0) then
  begin
    //Set Length of Button Array one Time Instead of one time for every Song
    SetButtonLength(Length(CatSongs.Song));

    I := 0;
    CreateSongButtons:

    try
      for Pet := I to High(CatSongs.Song) do begin // creating all buttons
        // new
        Texture.Limit := 512;// 256 0.4.2 value, 512 in 0.5.0

        if not FileExists(CatSongs.Song[Pet].Path + CatSongs.Song[Pet].Cover) then
          CatSongs.Song[Pet].Cover := ''; // 0.5.0: if cover not found then show 'no cover'

        if CatSongs.Song[Pet].Cover = '' then
          AddButton(300 + Pet*250, 140, 200, 200, Skin.GetTextureFileName('SongCover'), 'JPG', 'Plain', Theme.Song.Cover.Reflections)
        else begin
          // cache texture if there is a need to this
          if not Covers.CoverExists(CatSongs.Song[Pet].Path + CatSongs.Song[Pet].Cover) then begin
            Texture.CreateCacheMipmap := true;
            Texture.GetTexture(CatSongs.Song[Pet].Path + CatSongs.Song[Pet].Cover, 'Plain', true); // preloads textures and creates cache mipmap
            Texture.CreateCacheMipmap := false;

            // puts this texture to the cache file
            Covers.AddCover(CatSongs.Song[Pet].Path + CatSongs.Song[Pet].Cover);

            // unload full size texture
            Texture.UnloadTexture(CatSongs.Song[Pet].Path + CatSongs.Song[Pet].Cover, false);

            // we should also add mipmap texture by calling createtexture and use mipmap cache as data source
          end;

          // and now load it from cache file (small place for the optimization by eliminating reading it from file, but not here)
          AddButton(300 + Pet*250, 140, 200, 200, CatSongs.Song[Pet].Path + CatSongs.Song[Pet].Cover, 'JPG', 'Plain', Theme.Song.Cover.Reflections);
        end;
        Texture.Limit := 1024*1024;
        I := -1;
      end;
    except
      //When Error is reported the First time for this Song
      if (I <> Pet) then
      begin
        //Some Error reporting:
        Log.LogError('Could not load Cover: ' + CatSongs.Song[Pet].Cover);

        //Change Cover to NoCover and Continue Loading
        CatSongs.Song[Pet].Cover := '';
        I := Pet;
      end
      else //when Error occurs Multiple Times(NoSong Cover is damaged), then start loading next Song
      begin
        Log.LogError('NoCover Cover is damaged!');
        try
          AddButton(300 + Pet*250, 140, 200, 200, '', 'JPG', 'Plain', Theme.Song.Cover.Reflections);
        except
          Messagebox(0, PChar('No Cover Image is damage. Could not Workaround Song Loading, Ultrastar will exit now.'), PChar(Language.Translate('US_VERSION')), MB_ICONERROR or MB_OK);
          Halt;
        end;
        I := Pet + 1;
      end;
    end;

    if (I <> -1) then
      GoTo CreateSongButtons;

  end;

  // Randomize Patch
  Randomize;
  //Equalizer
  SetLength(EqualizerBands, Theme.Song.Equalizer.Bands);
  //ClearArray
  For I := low(EqualizerBands) to high(EqualizerBands) do
    EqualizerBands[I] := 3;

  if (Length(CatSongs.Song) > 0) then
    Interaction := 0;

  MP3Volume := Ini.PreviewVolume * 10;
end;

procedure TScreenSong.SetScroll;
var
  VS, B, I: Integer;
  VisArr:   array of Integer;

begin
  VS := CatSongs.VisibleSongs;
  if VS > 0 then
  begin
    //Set Positions
    Case Theme.Song.Cover.Style of
      3: SetScroll3;
      5:begin
          if VS > 5 then
            SetScroll5
          else
            SetScroll4;
        end;
      else SetScroll4;
    end;
    //Set Visibility of Video Icon
    Static[VideoIcon].Visible := (CatSongs.Song[Interaction].Video <> '');

    // Set visibility of medley icons
    if Mode=smNormal then
    begin
      Static[MedleyIcon].Visible := (CatSongs.Song[Interaction].Medley.Source = msTag);
      Static[CalcMedleyIcon].Visible := (CatSongs.Song[Interaction].Medley.Source = msCalculated);
    end else
    begin
      Static[MedleyIcon].Visible := false;
      Static[CalcMedleyIcon].Visible := false;
    end;

    //Set Texts:
    Text[TextArtist].Text := CatSongs.Song[Interaction].Artist;
    Text[TextTitle].Text  :=  CatSongs.Song[Interaction].Title;
    if (Mode=smNormal) then
    begin
      if (Ini.Tabs_at_startup = 1) And (CatSongs.CatNumShow = -1) then
      begin
        Text[TextNumber].Text := IntToStr(CatSongs.Song[Interaction].OrderNum) + '/' + IntToStr(CatSongs.CatCount);
        Text[TextTitle].Text  := '(' + IntToStr(CatSongs.Song[Interaction].CatNumber) + ' ' + Language.Translate('SING_SONGS_IN_CAT') + ')';
      end else if (CatSongs.CatNumShow = -2) then
        Text[TextNumber].Text := IntToStr(CatSongs.VisibleIndex(Interaction)+1) + '/' + IntToStr(VS)
      else if (CatSongs.CatNumShow = -3) then
        Text[TextNumber].Text := IntToStr(CatSongs.VisibleIndex(Interaction)+1) + '/' + IntToStr(VS)
      else if (Ini.Tabs_at_startup = 1) then
        Text[TextNumber].Text := IntToStr(CatSongs.Song[Interaction].CatNumber) + '/' +
          IntToStr(CatSongs.Song[Interaction - CatSongs.Song[Interaction].CatNumber].CatNumber)
      else
        Text[TextNumber].Text := IntToStr(Interaction+1) + '/' + IntToStr(Length(CatSongs.Song));
    end else if (Mode=smChallenge) and not PartyMedley then
    begin
      if (Ini.Tabs_at_startup = 1) and (CatSongs.CatNumShow = -1) then
      begin
        Text[TextNumber].Text := IntToStr(CatSongs.Song[Interaction].OrderNum) + '/' + IntToStr(CatSongs.CatCount);
        Text[TextTitle].Text  := '(' +
          IntToStr(CatSongs.Song[Interaction].CatNumber - PartySessionM2.GetSongsPlayed(CatSongs.Song[Interaction].OrderNum)) +
          '/' +
          IntToStr(CatSongs.Song[Interaction].CatNumber) + ' ' + Language.Translate('SING_SONGS_IN_CAT') + ')'; //AND HERE!
      end else if (CatSongs.CatNumShow = -2) then
        Text[TextNumber].Text := IntToStr(CatSongs.VisibleIndex(Interaction)+1) + '/' + IntToStr(VS)
      else if (CatSongs.CatNumShow = -3) then
      begin
        ChooseableSongs := VS - PartySessionM2.GetSongsPlayed(CatSongs.CatNumShow) - GetSongsSkipped();
        Text[TextNumber].Text := IntToStr(CatSongs.VisibleIndex(Interaction)+1) + '/' + IntToStr(VS) +
          ' (' + IntToStr(ChooseableSongs) + ')';
      end else if (Ini.Tabs_at_startup = 1) then
      begin
        ChooseableSongs:=CatSongs.Song[Interaction - CatSongs.Song[Interaction].CatNumber].CatNumber -
          PartySessionM2.GetSongsPlayed(CatSongs.CatNumShow) - GetSongsSkipped();

        Text[TextNumber].Text := IntToStr(CatSongs.Song[Interaction].CatNumber) + '/' +
          IntToStr(CatSongs.Song[Interaction - CatSongs.Song[Interaction].CatNumber].CatNumber) + ' (' +
          IntToStr(ChooseableSongs) + ')'; //HERE!
      end else
      begin
        ChooseableSongs:=Length(CatSongs.Song)-PartySessionM2.GetSongsPlayed(CatSongs.CatNumShow)-GetSongsSkipped();
        Text[TextNumber].Text := IntToStr(Interaction+1) + '/' + IntToStr(Length(CatSongs.Song)) + ' (' +
          IntToStr(ChooseableSongs) + ')'; //HERE!
      end
    end else if PartyMedley then//PartyMedley
    begin
      SetLength(VisArr, 0);

      if (CatSongs.CatNumShow = -2) then
      begin
        Text[TextNumber].Text := IntToStr(CatSongs.VisibleIndex(Interaction)+1) + '/' + IntToStr(VS);
      end
      else if (CatSongs.CatNumShow = -3) then //Playlist!
      begin
        for I := 0 to Length(CatSongs.Song) - 1 do
        begin
          if CatSongs.Song[I].Visible and not PartyPlayedMedley(I) and
            not SongSkipped(I) and (CatSongs.Song[I].Medley.Source>=MinSource) then
          begin
            SetLength(VisArr, Length(VisArr)+1);
            VisArr[Length(VisArr)-1] := I;
          end;
        end;
        ChooseableSongs := Length(VisArr);
        Text[TextNumber].Text := IntToStr(CatSongs.VisibleIndex(Interaction)+1) + '/' + IntToStr(VS) +
          ' (' + IntToStr(ChooseableSongs) + ')'; //HERE!
      end else if (Ini.Tabs_at_startup = 1) then
      begin
        for I := 0 to Length(CatSongs.Song) - 1 do
        begin
          if not CatSongs.Song[I].Main and not PartyPlayedMedley(I) and
            not SongSkipped(I) and (CatSongs.Song[I].Medley.Source>=MinSource) then
          begin
            if (PlaylistMan.Mode=0) or ((PlaylistMan.Mode<>0) and CatSongs.Song[I].Visible) then
            begin
              SetLength(VisArr, Length(VisArr)+1);
              VisArr[Length(VisArr)-1] := I;
            end;
          end;
        end;
        ChooseableSongs := Length(VisArr);

        Text[TextNumber].Text := IntToStr(CatSongs.Song[Interaction].CatNumber) + '/' +
          IntToStr(CatSongs.Song[Interaction - CatSongs.Song[Interaction].CatNumber].CatNumber) + ' (' +
          IntToStr(ChooseableSongs) + ')'; //HERE!
      end else
      begin
        for I := 0 to Length(CatSongs.Song) - 1 do
        begin
          if CatSongs.Song[I].Visible and not PartyPlayedMedley(I) and
            not SongSkipped(I) and (CatSongs.Song[I].Medley.Source>=MinSource) then
          begin
            SetLength(VisArr, Length(VisArr)+1);
            VisArr[Length(VisArr)-1] := I;
          end;
        end;
        ChooseableSongs := Length(VisArr);
        Text[TextNumber].Text := IntToStr(Interaction+1) + '/' + IntToStr(Length(CatSongs.Song)) + ' (' +
          IntToStr(ChooseableSongs) + ')'; //HERE!
      end;
    end else if (Mode=smParty) then//Party
    begin
      SetLength(VisArr, 0);

      if (CatSongs.CatNumShow = -2) then
      begin
        Text[TextNumber].Text := IntToStr(CatSongs.VisibleIndex(Interaction)+1) + '/' + IntToStr(VS);
      end
      else if (CatSongs.CatNumShow = -3) then //Playlist!
      begin
        for I := 0 to Length(CatSongs.Song) - 1 do
        begin
          if CatSongs.Song[I].Visible and not PartyPlayedSong(I) and not SongSkipped(I) then
          begin
            SetLength(VisArr, Length(VisArr)+1);
            VisArr[Length(VisArr)-1] := I;
          end;
        end;
        ChooseableSongs := Length(VisArr);
        Text[TextNumber].Text := IntToStr(CatSongs.VisibleIndex(Interaction)+1) + '/' + IntToStr(VS) +
          ' (' + IntToStr(ChooseableSongs) + ')'; //HERE!
      end else if (Ini.Tabs_at_startup = 1) then
      begin
        for I := 0 to Length(CatSongs.Song) - 1 do
        begin
          if not CatSongs.Song[I].Main and not PartyPlayedSong(I) and not SongSkipped(I) then
          begin
            if (PlaylistMan.Mode=0) or ((PlaylistMan.Mode<>0) and CatSongs.Song[I].Visible) then
            begin
              SetLength(VisArr, Length(VisArr)+1);
              VisArr[Length(VisArr)-1] := I;
            end;
          end;
        end;
        ChooseableSongs := Length(VisArr);

        Text[TextNumber].Text := IntToStr(CatSongs.Song[Interaction].CatNumber) + '/' +
          IntToStr(CatSongs.Song[Interaction - CatSongs.Song[Interaction].CatNumber].CatNumber) + ' (' +
          IntToStr(ChooseableSongs) + ')'; //HERE!
      end else
      begin
        for I := 0 to Length(CatSongs.Song) - 1 do
        begin
          if CatSongs.Song[I].Visible and not PartyPlayedSong(I) and not SongSkipped(I) then
          begin
            SetLength(VisArr, Length(VisArr)+1);
            VisArr[Length(VisArr)-1] := I;
          end;
        end;
        ChooseableSongs := Length(VisArr);
        Text[TextNumber].Text := IntToStr(Interaction+1) + '/' + IntToStr(Length(CatSongs.Song)) + ' (' +
          IntToStr(ChooseableSongs) + ')'; //HERE!
      end;
    end;
  end else
  begin
    Text[TextNumber].Text := '0/0';
    Text[TextArtist].Text := '';
    Text[TextTitle].Text  := '';
    for B := 0 to High(Button) do
    Button[B].Visible := False;

  end;
end;

procedure TScreenSong.SetScroll1;
var
  B:      integer;    // button
  BMin:   integer;    // button min
  BMax:   integer;    // button max
  Src:    integer;
//  Dst:    integer;
  Count:  integer;    // Dst is not used. Count is used.
  Ready:  boolean;

  VisCount: integer;  // count of visible (or selectable) buttons
  VisInt:   integer;  // visible position of interacted button
  Typ:      integer;  // 0 when all songs fits the screen
  Placed:   integer;  // number of placed visible buttons
begin
//  Src := 0;
//  Dst := -1;
  Count := 1;
  Typ := 0;
  Ready := false;
  Placed := 0;

  VisCount := 0;
  for B := 0 to High(Button) do
    if CatSongs.Song[B].Visible then Inc(VisCount);

  VisInt := 0;
  for B := 0 to Interaction-1 do
    if CatSongs.Song[B].Visible then Inc(VisInt);


  if VisCount <= 6 then begin
    Typ := 0;
  end else begin
    if VisInt <= 3 then begin
      Typ := 1;
      Count := 7;
      Ready := true;
    end;

    if (VisCount - VisInt) <= 3 then begin
      Typ := 2;
      Count := 7;
      Ready := true;
    end;

    if not Ready then begin
      Typ := 3;
      Src := Interaction;
    end;
  end;



  // hide all buttons
  for B := 0 to High(Button) do begin
    Button[B].Visible := false;
    Button[B].Selectable := CatSongs.Song[B].Visible;
  end;

{  for B := Src to Dst do begin
//    Button[B].Visible := true;
    Button[B].Visible := CatSongs.Song[B].Visible;
    Button[B].Selectable := Button[B].Visible;
    Button[B].Y := 140 + (B-Src) * 60;
  end;}


  if Typ = 0 then begin
    for B := 0 to High(Button) do begin
      if CatSongs.Song[B].Visible then begin
        Button[B].Visible := true;
        Button[B].Y := 140 + (Placed) * 60;
        Inc(Placed);
      end;
    end;
  end;

  if Typ = 1 then begin
    B := 0;
    while (Count > 0) do begin
      if CatSongs.Song[B].Visible then begin
        Button[B].Visible := true;
        Button[B].Y := 140 + (Placed) * 60;
        Inc(Placed);
        Dec(Count);
      end;
      Inc(B);
    end;
  end;

  if Typ = 2 then begin
    B := High(Button);
    while (Count > 0) do begin
      if CatSongs.Song[B].Visible then begin
        Button[B].Visible := true;
        Button[B].Y := 140 + (6-Placed) * 60;
        Inc(Placed);
        Dec(Count);
      end;
      Dec(B);
    end;
  end;

  if Typ = 3 then begin
    B := Src;
    Count := 4;
    while (Count > 0) do begin
      if CatSongs.Song[B].Visible then begin
        Button[B].Visible := true;
        Button[B].Y := 140 + (3+Placed) * 60;
        Inc(Placed);
        Dec(Count);
      end;
      Inc(B);
    end;

    B := Src-1;
    Placed := 0;
    Count := 3;
    while (Count > 0) do begin
      if CatSongs.Song[B].Visible then begin
        Button[B].Visible := true;
        Button[B].Y := 140 + (2-Placed) * 60;
        Inc(Placed);
        Dec(Count);
      end;
      Dec(B);
    end;

  end;

  if Length(Button) > 0 then
    Static[1].Texture.Y := Button[Interaction].Y - 5; // selection texture
end;

procedure TScreenSong.SetScroll2;
var
  B:      integer;
  Wsp:    integer; // wspolczynnik przesuniecia wzgledem srodka ekranu
  Wsp2:   real;
begin
  // liniowe
  for B := 0 to High(Button) do
    Button[B].X := 300 + (B - Interaction) * 260;

  if Length(Button) >= 3 then begin
    if Interaction = 0 then
      Button[High(Button)].X := 300 - 260;

    if Interaction = High(Button) then
      Button[0].X := 300 + 260;
  end;

  // kolowe
{  for B := 0 to High(Button) do begin
    Wsp := (B - Interaction); // 0 dla srodka, -1 dla lewego, +1 dla prawego itd.
    Wsp2 := Wsp / Length(Button);
    Button[B].X := 300 + 10000 * sin(2*pi*Wsp2);
//    Button[B].Y := 140 + 50 * ;
  end;}
end;

procedure TScreenSong.SetScroll3; // with slide
var
  B:      integer;
  Wsp:    integer; // wspolczynnik przesuniecia wzgledem srodka ekranu
  Wsp2:   real;
begin
  SongTarget := Interaction;

  // liniowe
  for B := 0 to High(Button) do
  begin
    Button[B].X := 300 + (B - SongCurrent) * 260;
    if (Button[B].X < -Button[B].W) OR (Button[B].X > 800) then
      Button[B].Visible := False
    else
      Button[B].Visible := True;
  end;

{  if Length(Button) >= 3 then begin
    if Interaction = 0 then
      Button[High(Button)].X := 300 - 260;

    if Interaction = High(Button) then
      Button[0].X := 300 + 260;
  end;}

  // kolowe
{  for B := 0 to High(Button) do begin
    Wsp := (B - Interaction); // 0 dla srodka, -1 dla lewego, +1 dla prawego itd.
    Wsp2 := Wsp / Length(Button);
    Button[B].X := 300 + 10000 * sin(2*pi*Wsp2);
//    Button[B].Y := 140 + 50 * ;
  end;}
end;

procedure TScreenSong.SetScroll4; // rotate
var
  B:      integer;
  Wsp:    real;
  Z, Z2:      real;
  VS:     integer;
begin
  VS := CatSongs.VisibleSongs; // 0.5.0 (I): cached, very important

  // kolowe
  for B := 0 to High(Button) do begin
    Button[B].Visible := CatSongs.Song[B].Visible; // nowe
    if Button[B].Visible then begin // 0.5.0 optimization for 1000 songs - updates only visible songs, hiding in tabs becomes useful for maintaing good speed

    Wsp := 2 * pi * (CatSongs.VisibleIndex(B) - SongCurrent) /  VS {CatSongs.VisibleSongs};// 0.5.0 (II): takes another 16ms

    Z := (1 + cos(Wsp)) / 2;
    Z2 := (1 + 2*Z) / 3;


    Button[B].X := Theme.Song.Cover.X + (0.185 * Theme.Song.Cover.H * VS * sin(Wsp)) * Z2 - ((Button[B].H - Theme.Song.Cover.H)/2); // 0.5.0 (I): 2 times faster by not calling CatSongs.VisibleSongs
    Button[B].Z := Z / 2 + 0.3;

    Button[B].W := Theme.Song.Cover.H * Z2;

//    Button[B].Y := {50 +} 140 + 50 - 50 * Z2;
    Button[B].Y := Theme.Song.Cover.Y  + (Theme.Song.Cover.H - Abs(Button[B].H)) * 0.7 ;
    Button[B].H := Button[B].W;
    end;
  end;
end;

procedure TScreenSong.SetScroll5; // rotate
var
  B:      integer;
  Angle:    real;
  Pos:    Real;
  VS:     integer;
  diff:     real;
  X:        Real;
begin
  VS := CatSongs.VisibleSongs; // cache Visible Songs
  {Vars
  Theme.Song.CoverW: Radius des Kreises
  Theme.Song.CoverX: X Pos Linke Kante des gew�hlten Covers
  Theme.Song.CoverX: Y Pos Obere Kante des gew�hlten Covers
  Theme.Song.CoverH: H�he der Cover

  (CatSongs.VisibleIndex(B) - SongCurrent)/VS = Abstand zum MIttleren Cover in %
  }

  //Change Pos of all Buttons
  for B := low(Button) to high(Button) do
  begin
    Button[B].Visible := CatSongs.Song[B].Visible; //Adjust Visability
    if Button[B].Visible then //Only Change Pos for Visible Buttons
    begin
      Pos := (CatSongs.VisibleIndex(B) - SongCurrent);
      if (Pos < -VS/2) then
        Pos := Pos + VS
      else if (Pos > VS/2) then
        Pos := Pos - VS;

      if (Abs(Pos) < 2.5) then {fixed Positions}
      begin
      Angle := Pi * (Pos / 5);
      //Button[B].Visible := False;

      Button[B].H := Abs(Theme.Song.Cover.H * cos(Angle*0.8));//Power(Z2, 3);

//      Button[B].Reflectionspacing := 15 * Button[B].H/Theme.Song.Cover.H;
      Button[B].DeSelectReflectionspacing := 15 * Button[B].H/Theme.Song.Cover.H;

      Button[B].Z := 0.95 - Abs(Pos) * 0.01;

      Button[B].Y := (Theme.Song.Cover.Y  + (Theme.Song.Cover.H - Abs(Theme.Song.Cover.H * cos(Angle))) * 0.5);

      Button[B].W := Button[B].H;

      Diff := (Button[B].H - Theme.Song.Cover.H)/2;


      X := Sin(Angle*1.3)*0.9;

      Button[B].X := Theme.Song.Cover.X + Theme.Song.Cover.W * X - Diff;

      end
      else
      begin {Behind the Front Covers}
//      Button[B].Visible := False;
//        if VS/2-abs(Pos)>VS*0.4 then Button[B].Visible := False;

        if Pos < 0 then
          Pos := (Pos - VS/2) /VS
        else
          Pos := (Pos + VS/2) /VS;

        Angle := pi * Pos*2;

        Button[B].Z := (0.4 - Abs(Pos/4)) -0.00001; //z < 0.49999 is behind the cover 1 is in front of the covers

        Button[B].H :=0.6*(Theme.Song.Cover.H-Abs(Theme.Song.Cover.H * cos(Angle/2)*0.8));//Power(Z2, 3);

        Button[B].W := Button[B].H;

        Button[B].Y := Theme.Song.Cover.Y  - (Button[B].H - Theme.Song.Cover.H)*0.75;

//        Button[B].Reflectionspacing := 15 * Button[B].H/Theme.Song.Cover.H;
        Button[B].DeSelectReflectionspacing := 15 * Button[B].H/Theme.Song.Cover.H;

        Diff := (Button[B].H - Theme.Song.Cover.H)/2;

        Button[B].X :=  Theme.Song.Cover.X+Theme.Song.Cover.H/2-Button[b].H/2 + (Theme.Song.Cover.H)*sin(Angle/2)*1.52;

      end;

      //Button[B].Y := (Theme.Song.Cover.Y  + (Theme.Song.Cover.H - Button[B].H)/1.5); //Cover at down border of the change field
//      Button[B].Y := (Theme.Song.Cover.Y  + (Theme.Song.Cover.H - Button[B].H) * 0.7);

    end;
  end;
end;

procedure TScreenSong.onShow;
var
  I:  Integer;

begin
  if Music.VocalRemoverActivated() then
    Music.DisableVocalRemover;

  if SongIndex<>Interaction then
    Music.Stop;

  PartyMedley := false;

  SungToEnd := false;
  if Mode = smMedley then
    Mode := smNormal;

  MakeMedley := false;

  StartTry := false;
  AspectHandler.changed := false;
  MP3VolumeHandler.changed := false;

  SetLength(PlaylistMedley.Song, 0);
  SetLength(SkippedSongs, 0);

  if Ini.Players <= 3 then PlayersPlay := Ini.Players + 1;
  if Ini.Players = 4 then PlayersPlay := 6;

  for I := 0 to 2 do
    Text[TextTop[I]].Visible := false;

  Static[StaticTop].Visible := false;

  //Cat Mod etc
  if (Ini.Tabs_at_startup = 1) AND (CatSongs.CatNumShow = -1) AND
    (PlaylistMan.Mode=0) then
  begin
    CatSongs.ShowCategoryList;
    SelectNext;
    //Show Cat in Top Left Mod
    HideCatTL;
  end else if (PlaylistMan.Mode=0) and (Ini.Tabs_at_startup = 1) AND (CatSongs.CatNumShow = -3) then
  begin
    //Find Category
    I := Interaction;
    while not catsongs.Song[I].Main  do
    begin
      Dec (I);
      if (I < low(catsongs.Song)) then
        break;
    end;
    if (I<= 1) then
      Interaction := high(catsongs.Song)
    else
    Interaction := I - 1;

    //Stop Music
    Music.Stop;

    //Stop Video
    acClose;
    VidVis := none;
    CatSongs.ShowCategoryList;

    //Show Cat in Top Left Mod
    HideCatTL;

    //Show Wrong Song when Tabs on Fix
    SelectNext;
    FixSelected;
  end else if (Mode<>smNormal) and (PlaylistMan.Mode=0) and (CatSongs.CatNumShow < -1) then
  begin
    //Atm: Set Empty Filter
    CatSongs.SetFilter('', 0);

    //Show Cat in Top Left Mod
    HideCatTL;
    Interaction := 0;

    //Show Wrong Song when Tabs on Fix
    SelectNext;
    FixSelected;

    ChangeMusic;
  end;


  //Playlist Mode
  if (Mode = smNormal) then
  begin
    ScreenSongMenu.Visible := False;

    //If Playlist Shown -> Select Next automatically
    if (CatSongs.CatNumShow = -3) then
    begin
      SelectNext;
    end;
    Text[TextP1].visible := false;
    Text[TextP2].visible := false;
    ID := 'ID_024';
    if not Help.SetHelpID(ID) then
      Log.LogError('No Entry for Help-ID ' + ID + ' (ScreenSong, smNormal)');
    Text[TextPlugin].Visible := false;
  end else if (Mode = smParty) then   //Party Mode classic
  begin
    //TODO: PartyMedley
    PartyMedley := PartySession.Rounds[PartySession.CurRound].Medley;

    //Show Menu directly in PartyMode
    //But only if selected in Options
    if (Ini.PartyPopup = 1) then
    begin
      ScreenSongMenu.MenuShow(SM_Party_Main);
    end;
    Text[TextP1].visible := false;
    Text[TextP2].visible := false;
    ID := 'ID_025';
    if not Help.SetHelpID(ID) then
      Log.LogError('No Entry for Help-ID ' + ID + ' (ScreenSong, smParty)');

    Text[TextPlugin].Visible := true;
    if PartyMedley then
      MinSource := msCalculated;

    Text[TextPlugin].Text := PartySession.Plugins[PartySession.Rounds[PartySession.CurRound].PluginNr].Name;
    SelectRandomSong;
  end else if (Mode = smChallenge) then //M2-MOD
  begin
    PartyMedley := PartySessionM2.Rounds[PartySessionM2.CurRound].Medley;

    ID := 'ID_026';
    if not Help.SetHelpID(ID) then
      Log.LogError('No Entry for Help-ID ' + ID + ' (ScreenSong, smChallenge)');

    if not PartyMedley then
    begin
      if (PlaylistMan.Mode=1) then  //One Category Select Category and Select Random Song
      begin
        CatSongs.ShowCategoryList;
        CatSongs.ClickCategoryButton(PlaylistMan.CurPlayList);
        ShowCatTL(PlaylistMan.CurPlayList);
      end else if (PlaylistMan.Mode=2) then
      begin
        PlaylistMan.SetPlayList(PlaylistMan.CurPlayList);
      end;

      RandomSongChallenge;
      //SkipTo(Random(CatSongs.VisibleSongs - PartySessionM2.GetSongsPlayed(CatSongs.CatNumShow)));

      if (Ini.Tabs_at_startup = 1) and (PlaylistMan.Mode <> 2) and
        (PlaylistMan.Mode <> 1) then
        FoundCAT:=false
      else
        FoundCAT:=true;
    end else
    begin
      MinSource := msCalculated;
      FoundCAT:=true;
      SelectRandomSong;
    end;

    Text[TextP1].Text := 'P1: ' + PartySessionM2.Teams.Teaminfo[0].Playerinfo[0].Name;
    Text[TextP2].Text := 'P2: ' + PartySessionM2.Teams.Teaminfo[1].Playerinfo[0].Name;
    Text[TextP1].visible := true;
    Text[TextP2].visible := true;
    if PartySessionM2.Option_Plugins then
    begin
      Text[TextPlugin].Visible := true;
      Text[TextPlugin].Text := PartySessionM2.Plugins[PartySessionM2.Rounds[PartySessionM2.CurRound].PluginNr].Name;
    end else
      Text[TextPlugin].Visible := false;
  end;

  if Length(CatSongs.Song) > 0 then begin
    if SongIndex<>Interaction then
      ChangeMusic
    else
    begin
      StartVideoPreview;
      CoverTime := 0;
      LoadTop;
    end;

    SongIndex := -1;
    SetScroll;
  end;

  SetJoker;
  SetStatics;
end;

procedure TScreenSong.RandomSongChallenge();
var
  VisArr: array of integer;
  I:      integer;
begin
  if (CatSongs.VisibleSongs - PartySessionM2.GetSongsPlayed(CatSongs.CatNumShow))=0 then
    PartySessionM2.ResetSongsPlayed(CatSongs.CatNumShow);

  SetLength(VisArr, 0);
  for I := 0 to Length(CatSongs.Song) - 1 do
  begin
    if not PartyMedley then
    begin
      if CatSongs.Song[I].Visible and not SongSkipped(I) and
        not PartySessionM2.SongPlayed(CatSongs.CatNumShow, I) then
      begin
        SetLength(VisArr, Length(VisArr)+1);
        VisArr[Length(VisArr)-1] := I;
      end;
    end else
    begin
      if CatSongs.Song[I].Visible and not SongSkipped(I) and
        not PartyPlayedMedley(I) and
        (CatSongs.Song[I].Medley.Source >= MinSource) then
      begin
        SetLength(VisArr, Length(VisArr)+1);
        VisArr[Length(VisArr)-1] := I;
      end;
    end;
  end;

  if (Length(VisArr)>0) then
  begin
    I := Random(Length(VisArr));
    I := VisArr[I];

    //Choose Song
    SkipTo2(I);
  end;
end;

procedure TScreenSong.onHide;
begin
  //When Music Fading is activated, Turn Music to 100 %
  If (Ini.PreviewVolume <> 100) or (Ini.PreviewFading <> 0) then
    Music.SetMusicVolume(100);

  //If Preview is deactivated: Load MUsicfile now
  If (Ini.PreviewVolume = 0) then
    Music.Open(CatSongs.Song[Interaction].Path + CatSongs.Song[Interaction].Mp3);

  //When hide then Stop Music (For Party Mode Popup on Exit)
  if (Display.NextScreen <> @ScreenSong) and (Display.NextScreen <> @ScreenSing) and
    (Display.NextScreen <> @ScreenSingModi) and (Music <> nil) then
    Music.Stop;
end;

procedure TScreenSong.DrawExtensions;
begin
  //Draw Song Menu
  if (ScreenSongMenu.Visible) then
  begin
    ScreenSongMenu.Draw;
  end
  else if (ScreenSongJumpto.Visible) then
  begin
    ScreenSongJumpto.Draw;
  end
end;

function TScreenSong.Draw: boolean;
var
  dx:   real;
  dt:   real;
  I, J:    Integer;
  Window: TRectCoords;
  Blend: real;
begin
  dx := SongTarget-SongCurrent;
  dt := TimeSkip*7;
  if dt > 1 then dt := 1;
  SongCurrent := SongCurrent + dx*dt;

{  if SongCurrent > Catsongs.VisibleSongs then begin
    SongCurrent := SongCurrent - Catsongs.VisibleSongs;
    SongTarget := SongTarget - Catsongs.VisibleSongs;
  end;}

//  Log.BenchmarkStart(5);
  SetScroll;
//  Log.BenchmarkEnd(5);
//  Log.LogBenchmark('SetScroll4', 5);

  //Fading Functions, Only if Covertime is under 5 Seconds
  If (CoverTime < 5) then
  begin
    // 0.5.0: cover fade
    if (CoverTime < 1) and (CoverTime + TimeSkip >= 1) then begin
      // load new texture
      Texture.GetTexture(Button[Interaction].Texture.Name, 'Plain', false);
      Button[Interaction].Texture.Alpha := 1;
      Button[Interaction].Texture2 := Texture.GetTexture(Button[Interaction].Texture.Name, 'Plain', false);
      Button[Interaction].Texture2.Alpha := 1;
    end;
    {
    //Song Fade
    if (CatSongs.VisibleSongs > 0) AND (Ini.PreviewVolume <> 0) AND (Not CatSongs.Song[Interaction].Main) AND (Ini.PreviewFading <> 0) then
    begin
      //Start Song Fade after a little Time, to prevent Song to be Played on Scrolling
      if (CoverTime < 0.2) and (CoverTime + TimeSkip >= 0.2) then
        Music.Play;

      //Update Song Volume
      if (CoverTime < Ini.PreviewFading) then
        Music.SetMusicVolume(Round (CoverTime * Ini.PreviewVolume / Ini.PreviewFading * 10))
      else
        Music.SetMusicVolume(Ini.PreviewVolume * 10);

    end;
    }

    //Update Fading Time
    CoverTime := CoverTime + TimeSkip;

    //Update Fading Texture
    Button[Interaction].Texture2.Alpha := (CoverTime - 1) * 1.5;
    if Button[Interaction].Texture2.Alpha > 1 then Button[Interaction].Texture2.Alpha := 1;

  end;


  //inherited Draw;
  //heres a little Hack, that causes the Statics
  //are Drawn after the Buttons because of some Blending Problems.
  //This should cause no Problems because all Buttons on this screen
  //Has Z Position.
  //Draw BG
  DrawBG;

  //Medley Playlist
  if Length(PlaylistMedley.Song)>4 then
    J := Length(PlaylistMedley.Song)-4
  else
    J := 0;

  for I := 1 to 4 do
  begin
    if (Length(PlaylistMedley.Song)>=I+J) and (MakeMedley or
      ((Mode=smParty) and not PartySession.Rounds[PartySession.CurRound].MedleySurprise) or
      ((Mode=smChallenge) and not PartySessionM2.Rounds[PartySessionM2.CurRound].MedleySurprise)) then
    begin
      Text[TextMedley[I]].Visible := true;
      Text[TextMedley[I]].Text := IntToStr(I+J)+') ' +
        CatSongs.Song[PlaylistMedley.Song[I-1+J]].Artist + '\n' +
        CatSongs.Song[PlaylistMedley.Song[I-1+J]].Title;
    end else
      Text[TextMedley[I]].Visible := false;
  end;

  //Medley Mode
  if (Mode=smNormal) then
  begin
    if MakeMedley then
    begin
      Text[TextPlugin].Text := 'Medley-Mode';
      Text[TextPlugin].Visible := true;
    end else
      Text[TextPlugin].Visible := false;
  end;

  //Instead of Draw FG Procedure:
  //We draw Buttons for our own
  for I := 0 to Length(Button) - 1 do
    Button[I].Draw;

  // Statics
  for I := 0 to Length(Static) - 1 do
    Static[I].Draw;

  // and texts
  for I := 0 to Length(Text) - 1 do
    Text[I].Draw;



  //Draw Equalizer
  if Theme.Song.Equalizer.Visible then
    DrawEqualizer;

  if (CatSongs.Song[Interaction].Main) or (CatSongs.VisibleSongs = 0) then
  begin
    acClose;
    VidVis := none;
  end;

  if UVideo.VideoOpened then
  begin
    Czas.Teraz := Czas.Teraz + TimeSkip;
    try
      acGetFrame(Czas.Teraz);

      if VidVis=windowed then
      begin
        Window.Left := Button[Interaction].X;
        Window.Right := Button[Interaction].X+Button[Interaction].W;
        Window.Upper := Button[Interaction].Y;
        Window.Lower := Button[Interaction].Y+Button[Interaction].H;
        Window.windowed := true;

        {if CoverTime>=Ini.PreviewFading then
        begin
          glColor4f(0, 0, 0, 1);

          glbegin(gl_quads);
            glVertex2f(Window.Left, Window.Upper);
            glVertex2f(Window.Left, Window.Lower);
            glVertex2f(Window.Right, Window.Lower);
            glVertex2f(Window.Right, Window.Upper);
          glEnd;
        end; }
        SetAspectCorrection(acoCrop);
        Blend := (CoverTime-1.75)/Ini.PreviewFading;
        if Blend<0 then
          Blend := 0
        else if Blend>1 then
          Blend := 1;

        acDrawGLi(ScreenAct, Window, Blend);
      end else if VidVis=full then
      begin
        acDrawGL(ScreenAct);
      end;

      //ResetAspectCorrection;

      if (Czas.Teraz>=Czas.Razem) then
      begin
        acClose;
        VidVis := none;
      end;
    except
      //If an Error occurs drawing: prevent Video from being Drawn again and Close Video
      log.LogError('Error drawing Video, Video has been disabled for this Song/Session.');
      Log.LogError('Corrupted File: ' + CatSongs.Song[Interaction].Video);
      try
        acClose;
        VidVis := none;
      except

      end;
    end;
  end else
    StartVideoPreview;

  if (VidVis = full) and AspectHandler.changed and
    (AspectHandler.change_time+3>Czas.Teraz) then
  begin
    DrawAspect;
  end else if AspectHandler.changed and
    (AspectHandler.change_time+3<Czas.Teraz) then
    AspectHandler.changed := false;

  if MP3VolumeHandler.changed and (MP3VolumeHandler.change_time+TimeSkip<3) then
  begin
    MP3VolumeHandler.change_time := MP3VolumeHandler.change_time + TimeSkip;
    DrawVolumeBar(10, 530, 780, 12, MP3Volume);
  end else
    MP3VolumeHandler.changed := false;

  if MakeMedley or PartyMedley then
  begin
    for I := 0 to 2 do
      Text[TextTop[I]].Visible := false;

    Static[StaticTop].Visible := false;
  end;
  
  DrawExtensions;
end;

procedure TScreenSong.DrawAspect();
var
  txt: PChar;
  w, h: real;
  str: string;

begin
  //draw quad
  glColor4f(0.7, 0.7, 0.7, 0.6);
  glEnable(GL_BLEND);
  glbegin(gl_quads);
    glVertex2f(270, 20);
    glVertex2f(270, 60);
    glVertex2f(530, 60);
    glVertex2f(530, 20);
  glEnd;
  glDisable(GL_BLEND);

  //print Text
  case UVideo.fAspectCorrection of
    acoStretch: str := Language.Translate('VIDEO_ASPECT_STRETCH');
    acoCrop: str := Language.Translate('VIDEO_ASPECT_CROP');
    acoLetterBox: str := Language.Translate('VIDEO_ASPECT_LETTER_BOX');
    else
      str := 'error';
  end;

  glColor4f(1, 1, 1, 1);

  h := 11*ScreenH/RenderH;
  SetFontStyle(1);
  SetFontItalic(false);
  SetFontSize(h);
  w := glTextWidth(PChar(str));

  SetFontPos (RenderW/2-w/2, 20);
  txt := Addr(str[1]);
  glPrint(txt);
end;

procedure TScreenSong.SelectNext;
var
  Skip, Skip2:   integer;
  I:      integer;
  VS:     Integer;
begin
  VS := CatSongs.VisibleSongs;

  if VS > 0 then
  begin
    UnLoadDetailedCover;

    Skip := 1;
    Skip2:= 0;
     
    if (Mode=smChallenge) and not PartyMedley then
    begin
      while (not CatSongs.Song[(Interaction + Skip + Skip2) mod Length(Interactions)].Visible or
        PartySessionM2.SongPlayed(CatSongs.CatNumShow, (Interaction + Skip + Skip2) mod Length(Interactions)) or
        SongSkipped((Interaction + Skip + Skip2) mod Length(Interactions))) do
      begin
        if not CatSongs.Song[(Interaction + Skip + Skip2) mod Length(Interactions)].Visible then
          Inc(Skip)
        else
          Inc(Skip2);
      end;
    end else if PartyMedley then
    begin
      while (not CatSongs.Song[(Interaction + Skip + Skip2) mod Length(Interactions)].Visible or
        (CatSongs.Song[(Interaction + Skip + Skip2) mod Length(Interactions)].Medley.Source < MinSource) or
        PartyPlayedMedley((Interaction + Skip + Skip2) mod Length(Interactions)) or
        SongSkipped((Interaction + Skip + Skip2) mod Length(Interactions))) do
      begin
        if not CatSongs.Song[(Interaction + Skip + Skip2) mod Length(Interactions)].Visible then
          Inc(Skip)
        else
          Inc(Skip2);
      end;
    end else if (Mode=smParty) then
    begin
      while (not CatSongs.Song[(Interaction + Skip + Skip2) mod Length(Interactions)].Visible or
        PartyPlayedSong((Interaction + Skip + Skip2) mod Length(Interactions)) or
        SongSkipped((Interaction + Skip + Skip2) mod Length(Interactions))) do
      begin
        if not CatSongs.Song[(Interaction + Skip + Skip2) mod Length(Interactions)].Visible then
          Inc(Skip)
        else
          Inc(Skip2);
      end;
    end else
    begin
      while (not CatSongs.Song[(Interaction + Skip + Skip2) mod Length(Interactions)].Visible or
        SongSkipped((Interaction + Skip + Skip2) mod Length(Interactions))) do
      begin
        if not CatSongs.Song[(Interaction + Skip + Skip2) mod Length(Interactions)].Visible then
          Inc(Skip)
        else
          Inc(Skip2);
      end;
    end;


    SongTarget := SongTarget + 1 + Skip2;//Skip;
    Interaction := (Interaction + Skip + Skip2) mod Length(Interactions);

    // try to keep all at the beginning
    if SongTarget > VS-1 then
    begin
      SongTarget := SongTarget - VS;
      SongCurrent := SongCurrent - VS;
    end;

  end;
      // Interaction -> Button, ktorego okladke przeczytamy
      //  Button[Interaction].Texture := Texture.GetTexture(Button[Interaction].Texture.Name, 'Plain', false); // 0.5.0: show uncached texture
end;

procedure TScreenSong.SelectPrev;
var
  Skip:   integer;
  VS:     Integer;
begin
  VS := CatSongs.VisibleSongs;

  if VS > 0 then
  begin
    UnLoadDetailedCover;

    Skip := 1;

    while (not CatSongs.Song[(Interaction - Skip + Length(Interactions)) mod Length(Interactions)].Visible) do Inc(Skip);
    SongTarget := SongTarget - 1;//Skip;

    Interaction := (Interaction - Skip + Length(Interactions)) mod Length(Interactions);

    // try to keep all at the beginning
    if SongTarget < 0 then begin
      SongTarget := SongTarget + CatSongs.VisibleSongs;
      SongCurrent := SongCurrent + CatSongs.VisibleSongs;
    end;

  //  Button[Interaction].Texture := Texture.GetTexture(Button[Interaction].Texture.Name, 'Plain', false); // 0.5.0: show uncached texture
  end;
end;

procedure TScreenSong.UpdateLCD;
begin
  LCD.HideCursor;
  LCD.Clear;
  LCD.WriteText(1, Text[TextArtist].Text);
  LCD.WriteText(2, Text[TextTitle].Text);
end;

//Procedure Change current played Preview
procedure TScreenSong.ChangeMusic;
var
  I:  integer;
begin
  //When Music Preview is avtivated -> then Change Music
  if (Ini.PreviewVolume <> 0) then
  begin
    if (NOT CatSongs.Song[Interaction].Main) AND(CatSongs.VisibleSongs > 0) then
    begin
      Music.Close;
      if Music.Open(CatSongs.Song[Interaction].Path + CatSongs.Song[Interaction].Mp3) then
      begin
        if (CatSongs.Song[Interaction].PreviewStart>0) then
          Music.MoveTo(CatSongs.Song[Interaction].PreviewStart)
        else
          Music.MoveTo(Music.Length / 4);

        StartVideoPreview;
        //If Song Fading is activated then don't Play directly, and Set Volume to Null, else Play normal
        if (Ini.PreviewFading = 0) then
        begin
          Music.SetMusicVolume (MP3Volume);
          Music.Play;
        end else
        begin
          Music.Fade(0, MP3Volume, Ini.PreviewFading);
          Music.Play;
        end;
      end;
    end else
      Music.Stop;
  end;

  LoadTop;
end;

procedure TScreenSong.LoadTop;
var
  I: integer;
begin
  //Load Top 3
  if (NOT CatSongs.Song[Interaction].Main) AND (CatSongs.VisibleSongs > 0) and
    not MakeMedley and not PartyMedley then
  begin
    AktSong := CatSongs.Song[Interaction];
    DataBase.ReadScore(AktSong, 3, {Ini.SumPlayers}0);

    for I := 0 to 2 do
    begin
      Text[TextTop[I]].Text := IntToStr(I+1)+'. ';
    end;

    if Length(AktSong.Score[Ini.Difficulty])>0 then
      Static[StaticTop].Visible := true
    else
      Static[StaticTop].Visible := false;

    for I := 0 to Length(AktSong.Score[Ini.Difficulty])-1 do
    begin
      Text[TextTop[I]].Visible := true;

      Text[TextTop[I]].Text := Text[TextTop[I]].Text + AktSong.Score[Ini.Difficulty, I].Name + '\n' +
        AktSong.Score[Ini.Difficulty, I].Date + ' (' + IntToStr(AktSong.Score[Ini.Difficulty, I].Score) + ')';
    end;

    for I := Length(AktSong.Score[Ini.Difficulty]) to 2 do
      Text[TextTop[I]].Visible := false;
  end else
  begin
    for I := 0 to 2 do
      Text[TextTop[I]].Visible := false;

    Static[StaticTop].Visible := false;
  end;
end;

procedure TScreenSong.StartVideoPreview;
begin
  if (Ini.PreviewVolume <> 0) and (Ini.MovieSize < 3) then
  begin
    if (NOT CatSongs.Song[Interaction].Main) AND (CatSongs.VisibleSongs > 0) then
    begin
      if CoverTime<0.75 then
      begin
        acClose;
        VidVis := none;
        StartTry := true;
        AspectHandler.changed := false;
      end else if (Ini.MoviePreview=1) and StartTry then
      begin
        if (CatSongs.Song[Interaction].Video <> '') and
          FileExists(CatSongs.Song[Interaction].Path + CatSongs.Song[Interaction].Video) then
        begin
          acOpenFile(PAnsiChar(CatSongs.Song[Interaction].Path + CatSongs.Song[Interaction].Video));

          acSkip2(CatSongs.Song[Interaction].VideoGAP, Music.Position);
          Czas.Teraz := Music.Position;
          Czas.Razem := Music.Length;
          StartTry := false;
          try
            acGetFrame(Czas.Teraz);
            VidVis := windowed;
          except
            //If an Error occurs Reading Video: prevent Video from being Drawn again and Close Video
            Log.LogError('Error drawing Video, Video has been disabled for this Song/Session.');
            Log.LogError('Corrupted File: ' + CatSongs.Song[Interaction].Video);
            CatSongs.Song[Interaction].Video := ''; //dirt fix
            try
              acClose;
              VidVis := none;
            except

            end;
          end;
        end else
          VidVis := none;
      end;
    end;
  end;
end;


procedure TScreenSong.SkipTo(Target: Cardinal); // 0.5.0
var
  I:      integer;
  
begin
  UnLoadDetailedCover;

  Interaction := High(CatSongs.Song);
  SongTarget := 0;

  for I := 1 to Target+1 do
    SelectNext;

  FixSelected2;
end;

procedure TScreenSong.SkipTo2(Target: Cardinal); //new
begin
  UnLoadDetailedCover;

  Interaction := High(CatSongs.Song);
  SongTarget := 0;

  while Interaction<>Target do
  begin
    SelectNext;
  end;

  FixSelected2;
end;

procedure TScreenSong.DrawEqualizer;
var
  Data: TFFTData; //Audio Data
  I, J: Integer;
  Res: byte;
  A, B: Integer;
  PosX, PosY: Integer;
  Pos: Real;
begin
if (not Music.Finished) AND (Theme.Song.Equalizer.Length > 0) then
begin


  A := GetTickCount div 44;

  if (A <> EqualizerTime) then
  begin
    EqualizerTime := A;
    Data := Music.GetFFTData;

    B:=0;
    Pos := 0;
    Res := ceil(92/Theme.Song.Equalizer.Bands);//How much channels are used for one Band

    //Change Lengths
    for I := 0 to (Res * Theme.Song.Equalizer.Bands - 1) do
    begin
      A := floor(I/Res);

      if (A<>B) then //Band changed
      begin
        if (Pos <= Theme.Song.Equalizer.Length) then
        begin
          if ((Pos < EqualizerBands[B]) AND (EqualizerBands[B]>1)) then
            EqualizerBands[B] := EqualizerBands[B] - 1
          else
            EqualizerBands[B] := floor(Pos);
        end
        else
          EqualizerBands[B] := 1;

        B := A;
        Pos := 0;
      end;

      if I > 35 then
        Data[i] := Data[i] * 8
      else if I > 11 then
        Data[i] := Data[i] * 4.5
      else
        Data[i] := Data[i] * 1.1;

      if (Data[i] >= 1) then
        Data[i] := 0.9999999999999;

      if Data[i]*Theme.Song.Equalizer.Length > Pos then
        Pos := Data[i]*Theme.Song.Equalizer.Length;
    end;

    //Change Last Band
    if (EqualizerBands[B] <= Theme.Song.Equalizer.Length) then
    begin
      if ((Pos < EqualizerBands[B]) AND (EqualizerBands[B]>1)) then
        EqualizerBands[B] := EqualizerBands[B] - 1
      else
        EqualizerBands[B] := floor(Pos)
    end
    else
      EqualizerBands[B] := 1;
  end;

  //Draw every Channel
  glColor4f(Theme.Song.Equalizer.ColR, Theme.Song.Equalizer.ColG, Theme.Song.Equalizer.ColB, Theme.Song.Equalizer.Alpha); //Set Color
  glDisable(GL_TEXTURE_2D);
  glEnable(GL_BLEND);

  PosY := Theme.Song.Equalizer.Y;
  PosX := Theme.Song.Equalizer.X;

  For I := 0 to Theme.Song.Equalizer.Bands-1 do
  begin
    if Theme.Song.Equalizer.Direction then
      PosY := Theme.Song.Equalizer.Y //+ (Theme.Song.Equalizer.H + Theme.Song.Equalizer.Space) * Theme.Song.Equalizer.Length
    else
      PosX := Theme.Song.Equalizer.X;
    //Draw for every visible quad
    for J := 1 to EqualizerBands[I] do
    begin
      glBegin(GL_QUADS);
        glVertex3f(PosX, PosY, Theme.Song.Equalizer.Z);
        glVertex3f(PosX, PosY+Theme.Song.Equalizer.H, Theme.Song.Equalizer.Z);
        glVertex3f(PosX+Theme.Song.Equalizer.W, PosY+Theme.Song.Equalizer.H, Theme.Song.Equalizer.Z);
        glVertex3f(PosX+Theme.Song.Equalizer.W, PosY, Theme.Song.Equalizer.Z);
      glEnd;

      if Theme.Song.Equalizer.Direction then //Vertically
        PosY := PosY - Theme.Song.Equalizer.H - Theme.Song.Equalizer.Space
      else //Horizontally
        PosX := PosX + Theme.Song.Equalizer.W + Theme.Song.Equalizer.Space;
    end;
    if Theme.Song.Equalizer.Direction then //Horizontally
      PosX := PosX + Theme.Song.Equalizer.W + Theme.Song.Equalizer.Space
    else //Vertically
      PosY := PosY + Theme.Song.Equalizer.H + Theme.Song.Equalizer.Space;
  end;
end;
end;

Procedure TScreenSong.SelectRandomSong;
var
  I, I2: Integer;
  VisArr: array of Integer;

begin
  if PartyMedley then
  begin
    if (Length(getVisibleMedleyArr(MinSource))<=0) then
      SetLength(MedleyPlayed, 0);
  end else
  begin
    if (CatSongs.VisibleSongs-Length(PartyPlayed)<=0) then
      SetLength(PartyPlayed, 0);
  end;

  Case PlaylistMan.Mode of
      0:  //All Songs Just Select Random Song
        begin
          //When Tabs are activated then use Tab Method
          if (Ini.Tabs_at_startup = 1) then
          begin
            SetLength(VisArr, 0);
            for I := 0 to Length(CatSongs.Song) - 1 do
            begin
              if not PartyMedley then
              begin
                if not CatSongs.Song[I].Main and not SongSkipped(I) and not PartyPlayedSong(I) then
                begin
                  SetLength(VisArr, Length(VisArr)+1);
                  VisArr[Length(VisArr)-1] := I;
                end;
              end else
              begin
                if not CatSongs.Song[I].Main and not SongSkipped(I) and not PartyPlayedMedley(I) and
                  (CatSongs.Song[I].Medley.Source >= MinSource) then
                begin
                  SetLength(VisArr, Length(VisArr)+1);
                  VisArr[Length(VisArr)-1] := I;
                end;
              end;
            end;

            if (Length(VisArr)>0) then
            begin
              I2 := Random(Length(VisArr));
              I2 := VisArr[I2];

              //Search Cat
              for I := I2 downto low(CatSongs.Song) do
              begin
                if CatSongs.Song[I].Main then
                  break;
              end;
              //In I ist jetzt die Kategorie in I2 der Song
              //I is the CatNum, I2 is the No of the Song within this Cat

              //Choose Cat
              CatSongs.ShowCategoryList;

              //Show Cat in Top Left Mod
              ShowCatTL (I);

              CatSongs.ClickCategoryButton(I);
              //SelectNext;

              //Choose Song
              SkipTo2(I2);
            end;
          end
          //When Tabs are deactivated use easy Method
          else
          begin
            SetLength(VisArr, 0);
            for I := 0 to Length(CatSongs.Song) - 1 do
            begin
              if not PartyMedley then
              begin
                if not CatSongs.Song[I].Main and not SongSkipped(I) and not PartyPlayedSong(I) then
                begin
                  SetLength(VisArr, Length(VisArr)+1);
                  VisArr[Length(VisArr)-1] := I;
                end;
              end else
              begin
                if not CatSongs.Song[I].Main and not SongSkipped(I) and not PartyPlayedMedley(I) and
                  (CatSongs.Song[I].Medley.Source >= MinSource) then
                begin
                  SetLength(VisArr, Length(VisArr)+1);
                  VisArr[Length(VisArr)-1] := I;
                end;
              end;
            end;
            if (Length(VisArr)>0) then
            begin
              I2 := Random(Length(VisArr));
              I2 := VisArr[I2];

              //Search Cat
              for I := I2 downto low(CatSongs.Song) do
              begin
                if CatSongs.Song[I].Main then
                  break;
              end;
              //Choose Song
              SkipTo2(I2);
            end;
          end;
        end;
      1:  //One Category Select Category and Select Random Song
        begin
          CatSongs.ShowCategoryList;
          CatSongs.ClickCategoryButton(PlaylistMan.CurPlayList);
          ShowCatTL(PlaylistMan.CurPlayList);

          SetLength(VisArr, 0);
          for I := 0 to Length(CatSongs.Song) - 1 do
          begin
            if not PartyMedley then
            begin
              if CatSongs.Song[I].Visible and not SongSkipped(I) and not PartyPlayedSong(I) then
              begin
                SetLength(VisArr, Length(VisArr)+1);
                VisArr[Length(VisArr)-1] := I;
              end;
            end else
            begin
              if CatSongs.Song[I].Visible and not SongSkipped(I) and not PartyPlayedMedley(I) and
                (CatSongs.Song[I].Medley.Source >= MinSource) then
              begin
                SetLength(VisArr, Length(VisArr)+1);
                VisArr[Length(VisArr)-1] := I;
              end;
            end;
          end;
          if (Length(VisArr)>0) then
            begin
              I2 := Random(Length(VisArr));
              I2 := VisArr[I2];

              //Search Cat
              for I := I2 downto low(CatSongs.Song) do
              begin
                if CatSongs.Song[I].Main then
                  break;
              end;
              //Choose Song
              SkipTo2(I2);
            end;
        end;
      2:  //Playlist: Select Playlist and Select Random Song
        begin
          PlaylistMan.SetPlayList(PlaylistMan.CurPlayList);

          SetLength(VisArr, 0);
          for I := 0 to Length(CatSongs.Song) - 1 do
          begin
            if not PartyMedley then
            begin
              if CatSongs.Song[I].Visible and not SongSkipped(I) and not PartyPlayedSong(I) then
              begin
                SetLength(VisArr, Length(VisArr)+1);
                VisArr[Length(VisArr)-1] := I;
              end;
            end else
            begin
              if CatSongs.Song[I].Visible and not SongSkipped(I) and not PartyPlayedMedley(I) and
                (CatSongs.Song[I].Medley.Source >= MinSource) then
              begin
                SetLength(VisArr, Length(VisArr)+1);
                VisArr[Length(VisArr)-1] := I;
              end;
            end;
          end;
          if (Length(VisArr)>0) then
          begin
            I2 := Random(Length(VisArr));
            I2 := VisArr[I2];

            //Search Cat
            for I := I2 downto low(CatSongs.Song) do
            begin
              if CatSongs.Song[I].Main then
                break;
            end;
            //Choose Song
            SkipTo2(I2);
          end;

          FixSelected2;
        end;
  end;

  Music.PlayChange;
  ChangeMusic;
  SetScroll;
  UpdateLCD;
end;

//do Joker in M2-MOD mode
procedure TScreenSong.DoJokerM2;
begin
  if (PartySessionM2.Teams.Teaminfo[0].Joker>0) then
  begin
    if (((not CatSongs.Song[Interaction].Main) or (Ini.Tabs_at_startup=0)) and (ChooseableSongs>1)) then
    begin
      if (FoundCAT) then Dec(PartySessionM2.Teams.Teaminfo[0].Joker);
        FoundCAT:=true;

      SetLength(SkippedSongs, Length(SkippedSongs)+1);
      SkippedSongs[Length(SkippedSongs)-1] := Interaction;
    end;

    if (ChooseableSongs > 1) then
    begin
      RandomSongChallenge;
      //SkipTo(Random(CatSongs.VisibleSongs - PartySessionM2.GetSongsPlayed(CatSongs.CatNumShow) - GetSongsSkipped()));
      SetJoker;

      Music.PlayChange;
      ChangeMusic;
      SetScroll4;
    end;
  end;
end;

procedure TScreenSong.SetJoker;
var
  h, x, y:    real;
  ptxt: pchar;
begin
  //If Party Mode
  if Mode = smParty then //Show Joker that are available
  begin
    if (PartySession.Teams.NumTeams >= 1) then
    begin
      Static[StaticTeam1Joker1].Visible := (PartySession.Teams.Teaminfo[0].Joker >= 1);
      if PartySession.Teams.Teaminfo[0].Joker > 5 then
      begin
        Text[TextNumJokerTeam1].Text := 'x'+IntToStr(PartySession.Teams.Teaminfo[0].Joker);
        Text[TextNumJokerTeam1].Visible := true;

        Static[StaticTeam1Joker2].Visible := False;
        Static[StaticTeam1Joker3].Visible := False;
        Static[StaticTeam1Joker4].Visible := False;
        Static[StaticTeam1Joker5].Visible := False;
      end else
      begin
        Text[TextNumJokerTeam1].Visible := false;
        Static[StaticTeam1Joker2].Visible := (PartySession.Teams.Teaminfo[0].Joker >= 2);
        Static[StaticTeam1Joker3].Visible := (PartySession.Teams.Teaminfo[0].Joker >= 3);
        Static[StaticTeam1Joker4].Visible := (PartySession.Teams.Teaminfo[0].Joker >= 4);
        Static[StaticTeam1Joker5].Visible := (PartySession.Teams.Teaminfo[0].Joker >= 5);
      end;
    end
    else
    begin
      Static[StaticTeam1Joker1].Visible := False;
      Static[StaticTeam1Joker2].Visible := False;
      Static[StaticTeam1Joker3].Visible := False;
      Static[StaticTeam1Joker4].Visible := False;
      Static[StaticTeam1Joker5].Visible := False;
      Text[TextNumJokerTeam1].Visible := false;
    end;

    if (PartySession.Teams.NumTeams >= 2) then
    begin
      Static[StaticTeam2Joker1].Visible := (PartySession.Teams.Teaminfo[1].Joker >= 1);

      if PartySession.Teams.Teaminfo[1].Joker > 5 then
      begin
        Text[TextNumJokerTeam2].Text := 'x'+IntToStr(PartySession.Teams.Teaminfo[1].Joker);
        Text[TextNumJokerTeam2].Visible := true;

        Static[StaticTeam2Joker2].Visible := False;
        Static[StaticTeam2Joker3].Visible := False;
        Static[StaticTeam2Joker4].Visible := False;
        Static[StaticTeam2Joker5].Visible := False;
      end else
      begin
        Text[TextNumJokerTeam2].Visible := false;
        Static[StaticTeam2Joker2].Visible := (PartySession.Teams.Teaminfo[1].Joker >= 2);
        Static[StaticTeam2Joker3].Visible := (PartySession.Teams.Teaminfo[1].Joker >= 3);
        Static[StaticTeam2Joker4].Visible := (PartySession.Teams.Teaminfo[1].Joker >= 4);
        Static[StaticTeam2Joker5].Visible := (PartySession.Teams.Teaminfo[1].Joker >= 5);
      end;
    end
    else
    begin
      Static[StaticTeam2Joker1].Visible := False;
      Static[StaticTeam2Joker2].Visible := False;
      Static[StaticTeam2Joker3].Visible := False;
      Static[StaticTeam2Joker4].Visible := False;
      Static[StaticTeam2Joker5].Visible := False;
      Text[TextNumJokerTeam2].Visible := false;
    end;

    if (PartySession.Teams.NumTeams >= 3) then
    begin
      Static[StaticTeam3Joker1].Visible := (PartySession.Teams.Teaminfo[2].Joker >= 1);

      if PartySession.Teams.Teaminfo[2].Joker > 5 then
      begin
        Text[TextNumJokerTeam3].Text := 'x'+IntToStr(PartySession.Teams.Teaminfo[2].Joker);
        Text[TextNumJokerTeam3].Visible := true;

        Static[StaticTeam3Joker2].Visible := False;
        Static[StaticTeam3Joker3].Visible := False;
        Static[StaticTeam3Joker4].Visible := False;
        Static[StaticTeam3Joker5].Visible := False;
      end else
      begin
        Text[TextNumJokerTeam3].Visible := false;
        Static[StaticTeam3Joker2].Visible := (PartySession.Teams.Teaminfo[2].Joker >= 2);
        Static[StaticTeam3Joker3].Visible := (PartySession.Teams.Teaminfo[2].Joker >= 3);
        Static[StaticTeam3Joker4].Visible := (PartySession.Teams.Teaminfo[2].Joker >= 4);
        Static[StaticTeam3Joker5].Visible := (PartySession.Teams.Teaminfo[2].Joker >= 5);
      end;
    end
    else
    begin
      Static[StaticTeam3Joker1].Visible := False;
      Static[StaticTeam3Joker2].Visible := False;
      Static[StaticTeam3Joker3].Visible := False;
      Static[StaticTeam3Joker4].Visible := False;
      Static[StaticTeam3Joker5].Visible := False;
      Text[TextNumJokerTeam3].Visible := false;
    end;
  end
  else if (Mode = smChallenge) then  //M2-MOD-mode
  begin
    if (PartySessionM2.Teams.NumTeams >= 1) then
    begin
      Static[StaticTeam1Joker1].Visible := (PartySessionM2.Teams.Teaminfo[0].Joker >= 1);
      Static[StaticTeam1Joker2].Visible := (PartySessionM2.Teams.Teaminfo[0].Joker >= 2);
      Static[StaticTeam1Joker3].Visible := (PartySessionM2.Teams.Teaminfo[0].Joker >= 3);
      Static[StaticTeam1Joker4].Visible := (PartySessionM2.Teams.Teaminfo[0].Joker >= 4);
      Static[StaticTeam1Joker5].Visible := (PartySessionM2.Teams.Teaminfo[0].Joker >= 5);
      Text[TextNumJokerTeam1].Visible := false;
    end
    else
    begin
      Static[StaticTeam1Joker1].Visible := false;
      Static[StaticTeam1Joker2].Visible := false;
      Static[StaticTeam1Joker3].Visible := false;
      Static[StaticTeam1Joker4].Visible := false;
      Static[StaticTeam1Joker5].Visible := false;
      Text[TextNumJokerTeam1].Visible := false;
    end;

    if (PartySessionM2.Teams.NumTeams >= 2) then
    begin
      Static[StaticTeam2Joker1].Visible := (PartySessionM2.Teams.Teaminfo[1].Joker >= 1);
      Static[StaticTeam2Joker2].Visible := (PartySessionM2.Teams.Teaminfo[1].Joker >= 2);
      Static[StaticTeam2Joker3].Visible := (PartySessionM2.Teams.Teaminfo[1].Joker >= 3);
      Static[StaticTeam2Joker4].Visible := (PartySessionM2.Teams.Teaminfo[1].Joker >= 4);
      Static[StaticTeam2Joker5].Visible := (PartySessionM2.Teams.Teaminfo[1].Joker >= 5);
      Text[TextNumJokerTeam2].Visible := false;
    end
    else
    begin
      Static[StaticTeam2Joker1].Visible := false;
      Static[StaticTeam2Joker2].Visible := false;
      Static[StaticTeam2Joker3].Visible := false;
      Static[StaticTeam2Joker4].Visible := false;
      Static[StaticTeam2Joker5].Visible := false;
      Text[TextNumJokerTeam2].Visible := false;
    end;

    if (PartySessionM2.Teams.NumTeams >= 3) then
    begin
      Static[StaticTeam3Joker1].Visible := (PartySessionM2.Teams.Teaminfo[2].Joker >= 1);
      Static[StaticTeam3Joker2].Visible := (PartySessionM2.Teams.Teaminfo[2].Joker >= 2);
      Static[StaticTeam3Joker3].Visible := (PartySessionM2.Teams.Teaminfo[2].Joker >= 3);
      Static[StaticTeam3Joker4].Visible := (PartySessionM2.Teams.Teaminfo[2].Joker >= 4);
      Static[StaticTeam3Joker5].Visible := (PartySessionM2.Teams.Teaminfo[2].Joker >= 5);
      Text[TextNumJokerTeam3].Visible := false;
    end
    else
    begin
      Static[StaticTeam3Joker1].Visible := false;
      Static[StaticTeam3Joker2].Visible := false;
      Static[StaticTeam3Joker3].Visible := false;
      Static[StaticTeam3Joker4].Visible := false;
      Static[StaticTeam3Joker5].Visible := false;
      Text[TextNumJokerTeam3].Visible := false;
    end;
  end else
  begin //Hide all
    Static[StaticTeam1Joker1].Visible := False;
    Static[StaticTeam1Joker2].Visible := False;
    Static[StaticTeam1Joker3].Visible := False;
    Static[StaticTeam1Joker4].Visible := False;
    Static[StaticTeam1Joker5].Visible := False;
    Text[TextNumJokerTeam1].Visible := false;

    Static[StaticTeam2Joker1].Visible := False;
    Static[StaticTeam2Joker2].Visible := False;
    Static[StaticTeam2Joker3].Visible := False;
    Static[StaticTeam2Joker4].Visible := False;
    Static[StaticTeam2Joker5].Visible := False;
    Text[TextNumJokerTeam2].Visible := false;

    Static[StaticTeam3Joker1].Visible := False;
    Static[StaticTeam3Joker2].Visible := False;
    Static[StaticTeam3Joker3].Visible := False;
    Static[StaticTeam3Joker4].Visible := False;
    Static[StaticTeam3Joker5].Visible := False;
    Text[TextNumJokerTeam3].Visible := false;
  end;
end;

procedure TScreenSong.SetStatics;
var
  I:       Integer;
  Visible: Boolean;
begin
  //Set Visibility of Party M2 Statics and Text
  Visible := (Mode = smChallenge);

  For I := 0 to high(StaticM2Party) do
    Static[StaticM2Party[I]].Visible := Visible;

  For I := 0 to high(TextM2Party) do
    Text[TextM2Party[I]].Visible := Visible;

  //Set Visibility of Party Statics and Text
  Visible := (Mode = smParty);

  For I := 0 to high(StaticParty) do
    Static[StaticParty[I]].Visible := Visible;

  For I := 0 to high(TextParty) do
    Text[TextParty[I]].Visible := Visible;

  //Set Visibility of Non Party Statics and Text
  Visible := (Mode = smNormal);

  For I := 0 to high(StaticNonParty) do
    Static[StaticNonParty[I]].Visible := Visible;

  For I := 0 to high(TextNonParty) do
    Text[TextNonParty[I]].Visible := Visible;

end;

//Procedures for Menu

procedure TScreenSong.StartSong;
begin
  CatSongs.Selected := Interaction;
  Music.Stop;
  //Party Mode
  if (Mode = smParty) or (Mode = smChallenge) then
  begin
    FadeTo(@ScreenSingModi);
  end
  else
  begin
    FadeTo(@ScreenSing);
  end;
end;

procedure TScreenSong.SelectPlayers;
begin
  CatSongs.Selected := Interaction;
  Music.Stop;
  acClose;
  VidVis := none;
  ScreenName.Goto_SingScreen := True;
  FadeTo(@ScreenName);
end;

procedure TScreenSong.OpenEditor;
begin
  if (Length(Songs.Song) > 0) and (not CatSongs.Song[Interaction].Main) AND (Mode = smNormal) then begin
    Music.Stop;
    acClose;
    VidVis := none;
    Music.PlayStart;
    ScreenEditSub.Path := CatSongs.Song[Interaction].Path;
    ScreenEditSub.FileName := CatSongs.Song[Interaction].FileName;
    ScreenEditSub.SongIndex := Interaction;
    FadeTo(@ScreenEditSub);
  end;
end;

//Team No of Team (0-5)
procedure TScreenSong.DoJoker (Team: Byte; SDL_ModState: Word);
begin
  if not PartyMedley and (ChooseableSongs>1) and
    (Mode = smParty) AND (PartySession.Teams.NumTeams >= Team + 1) AND (PartySession.Teams.Teaminfo[Team].Joker > 0) then
  begin
    //Joker spielen
    if (SDL_ModState <> KMOD_LALT) then
      Dec(PartySession.Teams.Teaminfo[Team].Joker);

    SetLength(SkippedSongs, Length(SkippedSongs)+1);
    SkippedSongs[Length(SkippedSongs)-1] := Interaction;

    SetLength(PartyPlayed, Length(PartyPlayed)+1);
    PartyPlayed[Length(PartyPlayed)-1] := Interaction;

    SelectRandomSong;
    SetJoker;
  end;

  if PartyMedley and (Mode=smChallenge) and (PartySessionM2.Teams.Teaminfo[0].Joker>0) then
  begin
    if (ChooseableSongs>1) then
    begin
      Dec(PartySessionM2.Teams.Teaminfo[0].Joker);

      SetLength(SkippedSongs, Length(SkippedSongs)+1);
      SkippedSongs[Length(SkippedSongs)-1] := Interaction;

      SetLength(MedleyPlayed, Length(MedleyPlayed)+1);
      MedleyPlayed[Length(MedleyPlayed)-1] := Interaction;

      SelectRandomSong;
      SetJoker;
    end;
  end;

  if PartyMedley and (Mode=smParty) and (PartySession.Teams.Teaminfo[Team].Joker>0) then
  begin
    if (ChooseableSongs>1) then
    begin
      if (SDL_ModState <> KMOD_LALT) then
        Dec(PartySession.Teams.Teaminfo[Team].Joker);

      SetLength(SkippedSongs, Length(SkippedSongs)+1);
      SkippedSongs[Length(SkippedSongs)-1] := Interaction;

      SetLength(MedleyPlayed, Length(MedleyPlayed)+1);
      MedleyPlayed[Length(MedleyPlayed)-1] := Interaction;

      SelectRandomSong;
      SetJoker;
    end;
  end;

end;

//Detailed Cover Unloading. Unloads the Detailed, uncached Cover of the cur. Song
procedure TScreenSong.UnLoadDetailedCover;
begin
  CoverTime := 0;
  
  Button[Interaction].Texture := Texture.GetTexture(Button[Interaction].Texture.Name, 'Plain', true); // 0.5.0: show cached texture
  Button[Interaction].Texture2.Alpha := 0;

  if Button[Interaction].Texture.Name <> Skin.GetTextureFileName('SongCover') then
    Texture.UnloadTexture(Button[Interaction].Texture.Name, false);
end;

procedure TScreenSong.Refresh;
begin {
CatSongs.Refresh;
CatSongs.ShowCategoryList;
Interaction := 0;
SelectNext;
FixSelected; }

end;

function TScreenSong.getVisibleMedleyArr(MinS: TMedleySource): TVisArr;
var
  I: integer;
  res: TVisArr;
begin
  SetLength(res, 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 >= MinS) then
      begin
        SetLength(res, Length(res)+1);
        res[Length(res)-1] := I;
      end;
    end;
  end else begin
    if PartyMedley then
    begin
      for I := 0 to Length(CatSongs.Song) - 1 do
      begin
        if (CatSongs.Song[I].Medley.Source >= MinS) and not PartyPlayedMedley(I)
          and not SongSkipped(I) then
        begin
          if (PlaylistMan.Mode=0) or ((PlaylistMan.Mode<>0) and CatSongs.Song[I].Visible)  then
          begin
            SetLength(res, Length(res)+1);
            res[Length(res)-1] := I;
          end;
        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 >= MinS) then
        begin
          SetLength(res, Length(res)+1);
          res[Length(res)-1] := I;
        end;
      end;
    end;
  end;
  Result := res;
end;

//start Medley round
procedure TScreenSong.StartMedley(num: integer; MinS: 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 (num>0) and not PartyMedley and not MakeMedley then
  begin
    VS := Length(getVisibleMedleyArr(MinS));
    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(MinS));
    end;
  end else if not PartyMedley and not MakeMedley then //start this song
  begin
    SetLength(PlaylistMedley.Song, 1);
    PlaylistMedley.Song[0] := Interaction;
    PlaylistMedley.NumMedleySongs := 1;
  end else if PartyMedley then //PartyMedley
  begin
    AddSong(Interaction);
    PlaylistMedley.NumMedleySongs := Length(PlaylistMedley.Song);

    SetLength(SkippedSongs, Length(SkippedSongs)+1);
    SkippedSongs[Length(SkippedSongs)-1] := Interaction;

    SetLength(MedleyPlayed, Length(MedleyPlayed)+1);
    MedleyPlayed[Length(MedleyPlayed)-1] := Interaction;

    if ((Mode=smParty) and (PartySession.Rounds[PartySession.CurRound].MedleySurprise)) or
      ((Mode=smChallenge) and (PartySessionM2.Rounds[PartySessionM2.CurRound].MedleySurprise)) then
    begin
      VS := Length(getVisibleMedleyArr(MinS));
      if VS < num then
      begin
        SetLength(MedleyPlayed, 0);
        VS := Length(getVisibleMedleyArr(MinS));
        if VS<num then
          PlaylistMedley.NumMedleySongs := VS
        else
          PlaylistMedley.NumMedleySongs := num;
      end else
      PlaylistMedley.NumMedleySongs := num;

      Randomize;

      //set up Playlist Medley
      for I := 1 to PlaylistMedley.NumMedleySongs - 1 do
      begin
        VS := GetNextSongNr(MinS);
        AddSong(VS);

        SetLength(MedleyPlayed, Length(MedleyPlayed)+1);
      MedleyPlayed[Length(MedleyPlayed)-1] := VS;
      end;
    end;
  end else if MakeMedley then
  begin
    if (CatSongs.Song[Interaction].Medley.Source>=MinS) then
    begin
      AddSong(Interaction);
      PlaylistMedley.NumMedleySongs := Length(PlaylistMedley.Song);
    end;
  end;

  if (Mode=smNormal) and not MakeMedley then
  begin
    Mode := smMedley;
    Music.Stop;
    //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 else if PartyMedley then
  begin
    if (PlaylistMedley.NumMedleySongs = num) then
    begin
      Music.Stop;
      FadeTo(@ScreenSingModi);
    end else if (ChooseableSongs=1) then
    begin
      VS := Length(getVisibleMedleyArr(MinS));
      if VS<1 then
      begin
        SetLength(MedleyPlayed, 0);
        VS := Length(getVisibleMedleyArr(MinS));
        if VS<1 then
        begin
          Music.Stop;
          FadeTo(@ScreenSingModi);
        end else
          SelectRandomSong;
      end;
    end else
      SelectRandomSong;
  end else if MakeMedley then
  begin
    if PlaylistMedley.NumMedleySongs=num then
    begin
      Mode := smMedley;
      Music.Stop;
      //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;
end;


end.