aboutsummaryrefslogblamecommitdiffstats
path: root/Game/Code/Screens/UScreenSong.pas
blob: bd4c7c87b6f3c047e483290040931d2274312659 (plain) (tree)
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
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
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045



















                                                                                                             

                                      

























































                                                                                                                   

                                                                                   

















































































                                                                                                          


                                                            


































                                                                        

































                                                                   


































































































































































































































                                                                                                                                                                                                            
                                    































































































































































                                                                                                 


                                                                     
                                        

                                                                        






































































































































































































































































































                                                                                                                         





                                                                                                                 







                                                                                                                         





                                                                                                                 







                                                                                                                         












































































                                                                                                                 









































































































































































































































































                                                                                                                                                                                         

                                                                                    











                                                                                                       

                                                                                    
                




                                                                                                 










                                                                                                         

                                                                                    








                                                                                                          
                                           




                                         
                                                                                                      




                                                          
                                                                                             





                                                 
                                                                                                       




                                                          
                                                                                              
                




                                                                                                 










                                                                                                         
                                                                                             































































































































































































































































































































































































































































































                                                                                                                                                                                                      





































                                                                                                                     










                                             


                                                                                         













                                                                   
                                     




























                                                                                  

                                                                                     






















































































































































































































































































                                                                                                                                            











                                                                                                 










































































































                                                                                                                                               
                                                           





























































































































































































                                                                                                                                      









                                                           










                                                        
                                                                                                    





                                                       
                                                                                                     










































                                                                          
                                                                                                    





                                                       
                                                                                                     

































                                                                     
                                                                                                 





                                                     
                                                                                                  






























                                                                   
                                                                                                 





                                                     
                                                                                                  












































































































































































































































































































































                                                                                                                          



                                                       












                                                                                            


                                                           














                                                                                         


                                                           













































                                                                                                                                    
                        
          
                                                 
            















                                                                                                              

























































































                                                                        


                                                         




                                                                                                    







                                                 






                                                         




                                                         




























                                                                    
                                                  


                               













                                                 

























                                                      
unit UScreenSong;

interface

uses
  UMenu, TextGL, SDL, UMusic, 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
      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;

      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 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
      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 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;
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 SongIndex<>Interaction then
    Music.Stop;

  PartyMedley := false;

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

  MakeMedley := false;

  StartTry := false;
  AspectHandler.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;

  //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;
    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 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;

  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;
  I:      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;
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 (Ini.PreviewVolume * 10);
          Music.Play;
        end else
        begin
          Music.Fade(0, Ini.PreviewVolume*10, Ini.PreviewFading);
          Music.Play;
        end;
      end;
    end else
      Music.Stop;
  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
var
  I:      integer;

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
  txt:  string;
  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].Visible and (CatSongs.Song[I].Medley.Source >= MinS) and not PartyPlayedMedley(I)
          and not SongSkipped(I) then
        begin
          SetLength(res, Length(res)+1);
          res[Length(res)-1] := I;
        end;
      end;
    end else
    begin
      for I := 0 to Length(CatSongs.Song) - 1 do
      begin
        if CatSongs.Song[I].Visible and (CatSongs.Song[I].Medley.Source >= 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.