aboutsummaryrefslogblamecommitdiffstats
path: root/Game/Code/Classes/UMusic.pas
blob: 7fae30177630d068c8b6c963e514d2f6d997bf3a (plain) (tree)
1
2
3
4
5
6
7
8
9
10









                                                                                            
                   










































                                                                                             

                                            









































                                                                         





                                                             


































































































                                                                                         
                                                                              






























                                                                                                                       

                      


























                                                      
 

                  
 






                                                                                           
                        










                                





















                                                                                             








                                                            
                        





                          

















                                                              








                                                                                        




                                                                        
                                       






                                                                   
                            












































                                                                                      
 















































































                                                                                             
                   
                                                      


                                                              
































                                                                               
 







                                                                
                            










                                    
 


















































                                                                                                                                         
                  














                                                        
                                               






























































































































































































































































































































                                                                                                                                               

                 























































                                                                       




















































                                                                                 
   
unit UMusic;

interface

uses Classes, MPlayer, Windows, Messages, SysUtils, Forms, ULog, USongs, Bass;//, DXSounds;

procedure InitializeSound;

type
  TPos = record
    CP:   integer;
    line: integer;
    note: integer;
  end;

  TSongMode = (smNormal, smParty, smChallenge, smMedley);

  TSoundCard = record
    Name:     string;
    Source:   array of string;
  end;

  TFFTData  = array [0..256] of Single;
  
  TCustomSoundEntry = record
    Filename: String;
    Handle: hStream;
  end;

  TMusic = class
    private
//      MediaPlayer:        TMediaPlayer;       // It will be replaced by another component;
{      MediaPlayerStart:   TMediaPlayer;       // or maybe not if done this way ;)
      MediaPlayerBack:    TMediaPlayer;
      MediaPlayerSwoosh:  TMediaPlayer;
      MediaPlayerChange:  TMediaPlayer;
      MediaPlayerOption:  TMediaPlayer;
      MediaPlayerClick:   TMediaPlayer;
      MediaPlayerDrum:    TMediaPlayer;
      MediaPlayerHihat:   TMediaPlayer;
      MediaPlayerClap:    TMediaPlayer;
      MediaPlayerShuffle: TMediaPlayer;}
      BassStart:          hStream;            // Wait, I've replaced this with BASS
      BassBack:           hStream;            // It has almost all features we need
      BassSwoosh:         hStream;
      BassChange:         hStream;            // Almost? It aleady has them all :)
      BassOption:         hStream;
      BassClick:          hStream;
      BassDrum:           hStream;
      BassHihat:          hStream;
      BassClap:           hStream;
      BassShuffle:        hStream;
      BassApplause:       hStream;

      BassVoices:         array of hStream;

      //Custom Sounds
      CustomSounds: array of TCustomSoundEntry;


      Loaded:             boolean;
      Loop:               boolean;
//    DXSound:  TDXSound;
//    Player:   TcmxMp3;
      DSP_VocalRemover:   HDSP;
    public
      Bass:               hStream;
      
//      SoundCard:          array of TSoundCard;
      procedure InitializePlayback;
      procedure InitializeRecord;
      procedure SetVolume(Volume: integer);
      procedure SetMusicVolume(Volume: integer);
      procedure Fade(InitVolume, TargetVolume: Integer; FadeTime: real);
      procedure FadeStop(FadeTime: real);
      procedure SetLoop(Enabled: boolean);
      function Open(Name: string): boolean; // true if succeed
      procedure Rewind;
      procedure MoveTo(Time: real);
      procedure Play;
      procedure Pause; //Pause Mod
      procedure Stop;
      procedure Close;
      function Finished: boolean;
      function Length: real;
      function Position: real;
      procedure PlayStart;
      procedure PlayBack;
      procedure PlaySwoosh;
      procedure PlayChange;
      procedure PlayOption;
      procedure PlayClick;
      procedure PlayDrum;
      procedure PlayHihat;
      procedure PlayClap;
      procedure PlayShuffle;
      procedure StopShuffle;
      procedure PlayApplause;

      function  VoicesOpen(Names: array of string): integer;
      procedure VoicesPlay;
      procedure VoicesStop;
      procedure VoicesClose;

      procedure CaptureStart;
      procedure CaptureStop;
      procedure CaptureCard(RecordI, PlayerLeft, PlayerRight: byte);
      procedure StopCard(Card: byte);
      function LoadPlayerFromFile(var MediaPlayer: TMediaPlayer; Name: string): boolean;
      function LoadSoundFromFile(var hStream: hStream; Name: string): boolean;

      //Equalizer
      function GetFFTData: TFFTData;

      //Custom Sounds
      function LoadCustomSound(const Filename: String): Cardinal;
      procedure PlayCustomSound(const Index: Cardinal);

      procedure EnableVocalRemover;
      procedure DisableVocalRemover;
      function VocalRemoverActivated(): boolean;

end;

const
  RecordSystem = 1;

function FindNote(beat: integer): TPos;

type
  TMuzyka = record
    Path:   string;
    Start:  integer;        // start of song in ms
//    BPM:    array of TBPM;
//    Gap:    real;
    IlNut:  integer;
    DlugoscNut:   integer;
//    WartoscNut:   integer;
  end;

  TCzesci = record          //Lines
    Akt:      integer;      // aktualna czesc utworu do rysowania
    High:     integer;
    Ilosc:    integer;
    Resolution: integer;
    NotesGAP: integer;
    Wartosc:  integer;
    Czesc:    array of record    //Line
      Start:    integer;
      StartNote:  integer;
      Lyric:      string;
      LyricWidth: real;
      Koniec:   integer;         //Ende?
      BaseNote: integer;
      HighNut:  integer;
      IlNut:    integer;
      TotalNotes: integer;
      Nuta:     array of record       //Note
        Color:      integer;
        Start:      integer;
        Dlugosc:    integer;
        Ton:        integer;
        TonGamy:    integer;
        Tekst:      string;         //Silbe?
        FreeStyle:  boolean;
        Wartosc:    integer;    // zwykla nuta x1, zlota nuta x2
        IsMedley:   boolean;     //just for editor
        IsStartPreview: boolean; //just for editor




      end;
    end;
  end;

  TCzas = record              // wszystko, co dotyczy aktualnej klatki
//    BajtowTotal:  integer;
//    BajtowTeraz:  integer;
//    BajtowNaSek:  integer;
    OldBeat:      integer;    // poprzednio wykryty beat w utworze
    AktBeat:      integer;    // aktualny beat w utworze
    MidBeat:      real;       // dokladny AktBeat

    // should not be used
//    OldHalf:      integer;    // poprzednio wykryta polowka
//    AktHalf:      integer;    // aktualna polowka w utworze
//    MidHalf:      real;       // dokladny AktHalf

    // now we use this for super synchronization!
    // only used when analyzing voice
    OldBeatD:     integer;    // poprzednio wykryty beat w utworze
    AktBeatD:     integer;    // aktualny beat w utworze
    MidBeatD:     real;       // dokladny AktBeatD
    FracBeatD:    real;       // fractional part of MidBeatD

    // we use this for audiable clicks
    OldBeatC:     integer;    // poprzednio wykryty beat w utworze
    AktBeatC:     integer;    // aktualny beat w utworze
    MidBeatC:     real;       // dokladny AktBeatC
    FracBeatC:    real;       // fractional part of MidBeatC


    OldCzesc:     array [0..1] of integer;    // poprzednio wyswietlana czesc
                              // akt jest w czesci.akt

    Teraz:        real;       // aktualny czas w utworze
    Razem:        real;       // caly czas utworu
//    TerazSek:     integer;
  end;

var
  Form:     TForm;
  Music:    TMusic;

  // muzyka
  Muzyka:   TMuzyka;

  // czesci z nutami;
  Czesci:   array of TCzesci;

  // czas
  Czas:     TCzas;

  fHWND:        Thandle;

const
  ModeStr:  array[TMPModes] of string = ('Not ready', 'Stopped', 'Playing', 'Recording', 'Seeking', 'Paused', 'Open');

implementation
uses UGraphic, URecord, UFiles, UIni, UMain, UThemes, UTime;

//from http://www.un4seen.com/forum/?topic=5943.0;hl=sbvocalcut16
procedure SBVocalCut16(handle: DWORD; channel: DWORD; buffer: Pointer; length: DWORD; user: DWORD); stdcall;
var
  i:        DWORD;
  dmch:     Smallint;
  lch, rch: PSmallint;

begin
  i := 0;
  lch := buffer;
  rch := buffer;
  Inc(rch);

  while (i < length) do
  begin
    dmch := round((((0 - lch^) + (rch^)) div 2)*1.5);

    lch^ := dmch;
    rch^ := dmch;

    Inc(lch, 2);
    Inc(rch, 2);
    Inc(i, SizeOf(SmallInt) * 2);
  end;
end;

function FindNote(beat: integer): TPos;
var
  I, J:   integer;
  found:  boolean;
  min:    integer;
  diff:   integer;

begin
  found := false;

  for I := 0 to length(Czesci[0].Czesc) - 1 do
  begin
    for J := 0 to length(Czesci[0].Czesc[I].Nuta) - 1 do
    begin
      if (beat>=Czesci[0].Czesc[I].Nuta[J].Start) and
        (beat<=Czesci[0].Czesc[I].Nuta[J].Start + Czesci[0].Czesc[I].Nuta[J].Dlugosc) then
      begin
        Result.CP := 0;
        Result.line := I;
        Result.note := J;
        found:=true;
        break;
      end;
    end;
  end;

  if found then //found exactly
    exit;

  if AktSong.isDuet then
  begin
    for I := 0 to length(Czesci[1].Czesc) - 1 do
    begin
      for J := 0 to length(Czesci[1].Czesc[I].Nuta) - 1 do
      begin
        if (beat>=Czesci[1].Czesc[I].Nuta[J].Start) and
          (beat<=Czesci[1].Czesc[I].Nuta[J].Start + Czesci[1].Czesc[I].Nuta[J].Dlugosc) then
        begin
          Result.CP := 1;
          Result.line := I;
          Result.note := J;
          found:=true;
          break;
        end;
      end;
    end;
  end;

  if found then //found exactly
    exit;

  min := high(integer);
  //second try (approximating)
  for I := 0 to length(Czesci[0].Czesc) - 1 do
  begin
    for J := 0 to length(Czesci[0].Czesc[I].Nuta) - 1 do
    begin
      diff := abs(Czesci[0].Czesc[I].Nuta[J].Start - beat);
      if diff<min then
      begin
        Result.CP := 0;
        Result.line := I;
        Result.note := J;
        min := diff;
      end;
    end;
  end;

  if AktSong.isDuet then
  begin
    for I := 0 to length(Czesci[1].Czesc) - 1 do
    begin
      for J := 0 to length(Czesci[1].Czesc[I].Nuta) - 1 do
      begin
        diff := abs(Czesci[1].Czesc[I].Nuta[J].Start - beat);
        if diff<min then
        begin
          Result.CP := 1;
          Result.line := I;
          Result.note := J;
          min := diff;
        end;
      end;
    end;
  end;
end;

procedure InitializeSound;
begin
  Log.LogStatus('Initializing Playback', 'InitializeSound');  Music.InitializePlayback;
  Log.LogStatus('Initializing Record', 'InitializeSound');    Music.InitializeRecord;
end;

procedure TMusic.InitializePlayback;
begin
  Log.BenchmarkStart(4);
  Log.LogStatus('Initializing Playback Subsystem', 'Music Initialize');
  Loaded := false;
  Loop := false;
  fHWND := Classes.AllocateHWND( nil);

  if BASS_Init(1, 44100, 0, fHWND, nil) = false then 
  begin
    Application.MessageBox ('Could not initialize BASS', 'Error');
    Exit;
  end;
  DSP_VocalRemover := 0;
  SetLength(BassVoices, 0);
  Log.BenchmarkEnd(4); Log.LogBenchmark('--> Bass Init', 4);

  // config playing buffer
  //BASS_SetConfig(BASS_CONFIG_UPDATEPERIOD, 0);
  //BASS_SetConfig(BASS_CONFIG_BUFFER, 100);

{  MediaPlayer := TMediaPlayer.Create( nil );
  MediaPlayer.ParentWindow := fHWND;
  MediaPlayer.Wait := true;}

  Log.LogStatus('Loading Sounds', 'Music Initialize');

{  LoadPlayerFromFile(MediaPlayerStart,  SoundPath + 'Common Start.mp3');
  LoadPlayerFromFile(MediaPlayerBack,   SoundPath + 'Common Back.mp3');
  LoadPlayerFromFile(MediaPlayerSwoosh, SoundPath + 'menu swoosh.mp3');
  LoadPlayerFromFile(MediaPlayerChange, SoundPath + 'select music change music.mp3');
  LoadPlayerFromFile(MediaPlayerOption, SoundPath + 'option change col.mp3');
  LoadPlayerFromFile(MediaPlayerClick,  SoundPath + 'rimshot022b.mp3');

  LoadPlayerFromFile(MediaPlayerDrum,   SoundPath + 'bassdrumhard076b.mp3');
  LoadPlayerFromFile(MediaPlayerHihat,  SoundPath + 'hihatclosed068b.mp3');
  LoadPlayerFromFile(MediaPlayerClap,   SoundPath + 'claps050b.mp3');

  LoadPlayerFromFile(MediaPlayerShuffle, SoundPath + 'Shuffle.mp3');}

  Log.BenchmarkStart(4);
  LoadSoundFromFile(BassStart,  SoundPath + 'Common Start.mp3');
  LoadSoundFromFile(BassBack,   SoundPath + 'Common Back.mp3');
  LoadSoundFromFile(BassSwoosh, SoundPath + 'menu swoosh.mp3');
  LoadSoundFromFile(BassChange, SoundPath + 'select music change music 50.mp3');
  LoadSoundFromFile(BassOption, SoundPath + 'option change col.mp3');
  LoadSoundFromFile(BassClick,  SoundPath + 'rimshot022b.mp3');

//  LoadSoundFromFile(BassDrum,   SoundPath + 'bassdrumhard076b.mp3');
//  LoadSoundFromFile(BassHihat,  SoundPath + 'hihatclosed068b.mp3');
//  LoadSoundFromFile(BassClap,   SoundPath + 'claps050b.mp3');

//  LoadSoundFromFile(BassShuffle, SoundPath + 'Shuffle.mp3');
  LoadSoundFromFile(BassApplause,  SoundPath + 'Applause.mp3');
  Log.BenchmarkEnd(4); Log.LogBenchmark('--> Loading Sounds', 4);
end;

procedure TMusic.InitializeRecord;
var
  S:        integer;

begin
  if RecordSystem = 1 then begin
    SetLength(Sound, 6 {max players});//Ini.Players+1);
    for S := 0 to High(Sound) do begin //Ini.Players do begin
      Sound[S] := TSound.Create;
      Sound[S].Num := S;
      Sound[S].BufferNew := TMemoryStream.Create;
      SetLength(Sound[S].BufferLong, 1);
      Sound[S].BufferLong[0] := TMemoryStream.Create;
      Sound[S].n := 4*1024;
    end;


    // check for recording devices;
    {device := 0;
    descr := BASS_RecordGetDeviceDescription(device);

    SetLength(SoundCard, 0);
    while (descr <> '') do begin
      SC := High(SoundCard) + 1;
      SetLength(SoundCard, SC+1);

      Log.LogAnalyze('Device #'+IntToStr(device)+': '+ descr);
      SoundCard[SC].Description := Descr;

      // check for recording inputs
      mic[device] := -1; // default to no change
      input := 0;
      BASS_RecordInit(device);
      Log.LogAnalyze('Input #' + IntToStr(Input) + ': ' + BASS_RecordGetInputName(input));
      flags := BASS_RecordGetInput(input);

      SetLength(SoundCard[SC].Input, 0);
      while (flags <> -1) do begin
        SCI := High(SoundCard[SC].Input) + 1;
        SetLength(SoundCard[SC].Input, SCI+1);

        Log.LogAnalyze('Input #' + IntToStr(Input) + ': ' + BASS_RecordGetInputName(input));
        SoundCard[SC].Input[SCI].Name := BASS_RecordGetInputName(Input);

        if (flags and BASS_INPUT_TYPE_MASK) = BASS_INPUT_TYPE_MIC then begin
          mic[device] := input; // auto set microphone
        end;
        Inc(Input);
        flags := BASS_RecordGetInput(input);
      end;

      if mic[device] <> -1 then begin
        Log.LogAnalyze('Found the mic at input ' + IntToStr(Mic[device]))
      end else begin
        Log.LogAnalyze('Mic not found');
        mic[device] := 0; // setting to the first one (for kxproject)
      end;
      SoundCard[SC].InputSeleceted := Mic[Device];


      BASS_RecordFree;

      inc(Device);
      descr := BASS_RecordGetDeviceDescription(Device);
    end; // while}
  end; // if
end;

procedure TMusic.SetVolume(Volume: integer);
begin
  //Old Sets Wave Volume
  //BASS_SetVolume(Volume);
  //New: Sets Volume only for this Application
  BASS_SetConfig(BASS_CONFIG_GVOL_SAMPLE, Volume);
  BASS_SetConfig(BASS_CONFIG_GVOL_STREAM, Volume);
  BASS_SetConfig(BASS_CONFIG_GVOL_MUSIC, Volume);
end;

procedure TMusic.SetMusicVolume(Volume: Integer);
begin
  //Max Volume Prevention
  if Volume > 100 then
    Volume := 100;

  //Set MP3 Volume
  BASS_ChannelSetAttributes (Bass, -1, Volume, -101);

  //Set Applause Volume
  BASS_ChannelSetAttributes (BassApplause, -1, Volume, -101);
end;

procedure TMusic.Fade(InitVolume, TargetVolume: Integer; FadeTime: real);
var
  time: dword;
begin
  //Max Volume Prevention
  if TargetVolume > 100 then
    TargetVolume := 100
  else if TargetVolume<0 then
    TargetVolume := 0;

  BASS_ChannelSetAttributes (Bass, -1, InitVolume, -101);
  time := round(FadeTime*1000);
  BASS_ChannelSlideAttributes (Bass, -1, TargetVolume, -101, time);
end;

procedure TMusic.FadeStop(FadeTime: real);
var
  time: dword;
begin
  time := round(FadeTime*1000);
  BASS_ChannelSlideAttributes (Bass, -1, -2, -101, time);  //fade out and stop
end;

procedure TMusic.SetLoop(Enabled: boolean);
begin
  Loop := Enabled;
end;

function TMusic.Open(Name: string): boolean;
begin
  Loaded := false;

  if FileExists(Name) then begin
{    MediaPlayer.FileName := Name;
    MediaPlayer.Open;}

    Bass := Bass_StreamCreateFile(false, pchar(Name), 0, 0, 0);
    Loaded := true;
    DSP_VocalRemover:=0;
    //Set Max Volume
    //SetMusicVolume (100);
  end;

  Result := Loaded;

//  Player := TcmxMp3.Create(Name);
end;

procedure TMusic.Rewind;
begin
  if Loaded then begin
//    MediaPlayer.Position := 0;

  end;
end;

procedure TMusic.MoveTo(Time: real);
var
  bytes:    integer;
begin
//  if Loaded then begin
//    MediaPlayer.StartPos := Round(Time);
    bytes := BASS_ChannelSeconds2Bytes(Bass, Time);
    BASS_ChannelSetPosition(Bass, bytes);
//  end;
end;

procedure TMusic.Play;
begin
  if Loaded then begin
//    MediaPlayer.Play;
    if Loop then BASS_ChannelPlay(Bass, True); // start from beginning... actually bass itself does not loop, nor does this TMusic Class
    BASS_ChannelPlay(Bass, False); // for setting position before playing
  end;
end;

procedure TMusic.Pause; //Pause Mod
begin
  if Loaded then begin
    BASS_ChannelPause(Bass); // Pauses Song
  end;
end;

procedure TMusic.Stop;
begin
  Bass_ChannelStop(Bass);
//  Bass_StreamFree(Bass);
//  if ModeStr[MediaPlayer.Mode] = 'Playing' then begin
//    MediaPlayer.Stop;
//  end;
end;

procedure TMusic.Close;
begin
  Bass_StreamFree(Bass);
  DSP_VocalRemover:=0;
//  Player.Free;
//  MediaPlayer.Close;
end;

function TMusic.Length: real;
var
  bytes:    integer;
begin
  //Result := 60;

  bytes  := BASS_ChannelGetLength( Bass );
  Result := BASS_ChannelBytes2Seconds(Bass, bytes);

{  if Assigned(MediaPlayer) then begin
    if Loaded then Result := MediaPlayer.Length / 1000;
  end;}
//  if Assigned(Player) then
//    Result := Player.LengthInSeconds;
end;

function TMusic.Position: real;
var
  bytes:    integer;
begin
  //Result := 0;//MediaPlayer.Position / 1000;
  bytes := BASS_ChannelGetPosition(BASS);
  Result := BASS_ChannelBytes2Seconds(BASS, bytes);
end;

function TMusic.Finished: boolean;
begin
  Result := false;
//  if ModeStr[MediaPlayer.Mode] = 'Stopped' then Result := true;
  if BASS_ChannelIsActive(BASS) = BASS_ACTIVE_STOPPED then begin
//    beep;
    Result := true;
  end;
end;

{function myeffect( chan : integer; stream : Pointer; len : integer; udata : Pointer ): Pointer; cdecl;
var
  dane:   pwordarray;
  pet:    integer;
  Prev:     smallint;
  PrevNew:  smallint;
begin
  dane := stream;
  Prev := 0;
  for pet := 0 to len div 2 -1 do begin
    PrevNew := Dane[Pet];

//    Dane[pet] := Round(PrevNew*1/8 + Prev*7/8);

    Prev := Dane[Pet];
  end;
end;}

procedure TMusic.PlayStart;
{var
  Music:    PMix_Chunk;}
begin
{  Mix_OpenAudio(44100, 16, 1, 16*1024);
  Music := Mix_LoadWAV('D:\Rozne\UltraStar\Old\Boys - Hej Sokoly 30s.wav');
  Mix_RegisterEffect(0, myeffect, nil, 0);
  Mix_PlayChannel(0, Music, 0);}

//  MediaPlayerStart.Rewind;
//  MediaPlayerStart.Play;
  BASS_ChannelPlay(BassStart, True);
end;

procedure TMusic.PlayBack;
begin
//  MediaPlayerBack.Rewind;
//  MediaPlayerBack.Play;
//  if not
  BASS_ChannelPlay(BassBack, True);// then
//    Application.MessageBox ('Error playing stream!', 'Error');
end;

procedure TMusic.PlaySwoosh;
begin
//  MediaPlayerSwoosh.Rewind;
//  MediaPlayerSwoosh.Play;
  BASS_ChannelPlay(BassSwoosh, True);
end;

procedure TMusic.PlayChange;
begin
//  MediaPlayerChange.Rewind;
//  MediaPlayerChange.Play;
  BASS_ChannelPlay(BassChange, True);
end;

procedure TMusic.PlayOption;
begin
//  MediaPlayerOption.Rewind;
//  MediaPlayerOption.Play;
  BASS_ChannelPlay(BassOption, True);
end;

procedure TMusic.PlayClick;
begin
//  MediaPlayerClick.Rewind;
//  MediaPlayerClick.Play;
  BASS_ChannelPlay(BassClick, True);
end;

procedure TMusic.PlayDrum;
begin
//  MediaPlayerDrum.Rewind;
//  MediaPlayerDrum.Play;
  BASS_ChannelPlay(BassDrum, True);
end;

procedure TMusic.PlayHihat;
begin
//  MediaPlayerHihat.Rewind;
//  MediaPlayerHihat.Play;
  BASS_ChannelPlay(BassHihat, True);
end;

procedure TMusic.PlayClap;
begin
//  MediaPlayerClap.Rewind;
//  MediaPlayerClap.Play;
  BASS_ChannelPlay(BassClap, True);
end;

procedure TMusic.PlayShuffle;
begin
//  MediaPlayerShuffle.Rewind;
//  MediaPlayerShuffle.Play;
  BASS_ChannelPlay(BassShuffle, True);
end;

procedure TMusic.StopShuffle;
begin
  BASS_ChannelStop(BassShuffle);
end;

procedure TMusic.PlayApplause;
begin
//  MediaPlayerShuffle.Rewind;
//  MediaPlayerShuffle.Play;
  BASS_ChannelPlay(BassApplause, True);
end;

procedure TMusic.CaptureStart;
var
  S:        integer;
  SC:       integer;
  P1:       integer;
  P2:       integer;
begin
  for S := 0 to High(Sound) do
    Sound[S].BufferLong[0].Clear;

{    case PlayersPlay of
      1:  begin
            CaptureCard(0, 0, 1, 0);
          end;
      2:  begin
            if Ini.TwoPlayerMode = 0 then begin
              CaptureCard(0, 0, 1, 2);
            end else begin
              CaptureCard(0, 0, 1, 0);
              CaptureCard(1, 1, 2, 0);
            end;
          end;
      3:  begin
            CaptureCard(0, 0, 1, 2);
            CaptureCard(1, 1, 3, 0);
          end;
    end; // case}

//  CaptureCard(0, 0, 0, 0);
//  end;

  {for SC := 0 to High(SoundCard) do begin
    P1 := Ini.SoundCard[SC, 1];
    P2 := Ini.SoundCard[SC, 2];
    if P1 > PlayersPlay then P1 := 0;
    if P2 > PlayersPlay then P2 := 0;
    CaptureCard(SC, P1, P2);
  end;       }
  // 0.5.2: new
  for SC := 0 to High(Ini.CardList) do begin
    P1 := Ini.CardList[SC].ChannelL;
    P2 := Ini.CardList[SC].ChannelR;
    if P1 > PlayersPlay then P1 := 0;
    if P2 > PlayersPlay then P2 := 0;
    if (P1 > 0) or (P2 > 0) then
      CaptureCard(SC, P1, P2);
  end;
end;

procedure TMusic.CaptureStop;
var
  SC:   integer;
  P1:       integer;
  P2:       integer;
begin
{  if RecordSystem = 1 then begin
    case PlayersPlay of
      1:  begin
            StopCard(0);
          end;
      2:  begin
            if Ini.TwoPlayerMode = 0 then begin
              StopCard(0);
            end else begin
              StopCard(0);
              StopCard(1);
            end;
          end;
      3:  begin
            StopCard(0);
            StopCard(1);
          end;
    end;
  end;}

  {for SC := 0 to High(SoundCard) do begin
    StopCard(SC);
  end; }

  // 0.5.2
  for SC := 0 to High(Ini.CardList) do begin
    P1 := Ini.CardList[SC].ChannelL;
    P2 := Ini.CardList[SC].ChannelR;
    if P1 > PlayersPlay then P1 := 0;
    if P2 > PlayersPlay then P2 := 0;
    if (P1 > 0) or (P2 > 0) then StopCard(SC);
  end;

end;

//procedure TMusic.CaptureCard(RecordI, SoundNum, PlayerLeft, PlayerRight: byte);
procedure TMusic.CaptureCard(RecordI, PlayerLeft, PlayerRight: byte);
var
  Error:      integer;
  ErrorMsg:   string;
begin
  if not BASS_RecordInit(RecordI) then begin
    Error := BASS_ErrorGetCode;

    ErrorMsg := IntToStr(Error);
    if Error = BASS_ERROR_DX then ErrorMsg := 'No DX5';
    if Error = BASS_ERROR_ALREADY then ErrorMsg := 'The device has already been initialized';
    if Error = BASS_ERROR_DEVICE then ErrorMsg := 'The device number specified is invalid';
    if Error = BASS_ERROR_DRIVER then ErrorMsg := 'There is no available device driver';

    {Log.LogAnalyze('Error initializing record [' + IntToStr(RecordI) + ', '
      + IntToStr(PlayerLeft) + ', '+ IntToStr(PlayerRight) + ']: '
      + ErrorMsg);}
    Log.LogError('Error initializing record [' + IntToStr(RecordI) + ', '
      + IntToStr(PlayerLeft) + ', '+ IntToStr(PlayerRight) + ']: '
      + ErrorMsg);
    Log.LogError('Music -> CaptureCard: Error initializing record: ' + ErrorMsg);


  end else begin

  //SoundCard[RecordI].BassRecordStream := BASS_RecordStart(44100, 2, MakeLong(0, 20) , @GetMicrophone, PlayerLeft + PlayerRight*256);
  Recording.SoundCard[RecordI].BassRecordStream := BASS_RecordStart(44100, 2, MakeLong(0, 20) , @GetMicrophone, PlayerLeft + PlayerRight*256);

  {if SoundCard[RecordI].BassRecordStream = 0 then begin
    Error := BASS_ErrorGetCode;

    ErrorMsg := IntToStr(Error);
    if Error = BASS_ERROR_INIT then ErrorMsg := 'Not successfully called';
    if Error = BASS_ERROR_ALREADY then ErrorMsg := 'Recording is already in progress';
    if Error = BASS_ERROR_NOTAVAIL then ErrorMsg := 'The recording device is not available';
    if Error = BASS_ERROR_FORMAT then ErrorMsg := 'The specified format is not supported';
    if Error = BASS_ERROR_MEM then ErrorMsg := 'There is insufficent memory';
    if Error = BASS_ERROR_UNKNOWN then ErrorMsg := 'Unknown';

    Log.LogError('Error creating record stream [' + IntToStr(RecordI) + ', '
      + IntToStr(PlayerLeft) + ', '+ IntToStr(PlayerRight) + ']: '
      + ErrorMsg);
  end;         }
  end;
end;

procedure TMusic.StopCard(Card: byte);
begin
  BASS_RecordSetDevice(Card);
  BASS_RecordFree;
end;

function TMusic.LoadPlayerFromFile(var MediaPlayer: TMediaPlayer; Name: string): boolean;
begin
  Log.LogStatus('Loading Sound: "' + Name + '"', 'LoadPlayerFromFile');
  if FileExists(Name) then begin
    try
      MediaPlayer := TMediaPlayer.Create( nil );
    except
      Log.LogError('Failed to create MediaPlayer', 'LoadPlayerFromFile');
    end;
    try
      MediaPlayer.ParentWindow := fHWND;
      MediaPlayer.Wait := true;
      MediaPlayer.FileName := Name;
      MediaPlayer.DeviceType := dtAutoSelect;
      MediaPlayer.Display := nil;
    except
      Log.LogError('Failed setting MediaPlayer: ' + MediaPlayer.ErrorMessage, 'LoadPlayerFromFile');
    end;
    try
      MediaPlayer.Open;
    except
      Log.LogError('Failed to open using MediaPlayer', 'LoadPlayerFromFile');
    end;
  end else begin
    Log.LogError('Sound not found: "' + Name + '"', 'LoadPlayerFromFile');
    exit;
  end;
end;

function TMusic.LoadSoundFromFile(var hStream: hStream; Name: string): boolean;
var
  L: Integer;
begin
  if FileExists(Name) then begin
    Log.LogStatus('Loading Sound: "' + Name + '"', 'LoadPlayerFromFile');
    try
      hStream := BASS_StreamCreateFile(False, pchar(Name), 0, 0, 0);
      //Add CustomSound
      L := High(CustomSounds) + 1;
      SetLength (CustomSounds, L + 1);
      CustomSounds[L].Filename := Name;
      CustomSounds[L].Handle := hStream;
    except
      Log.LogError('Failed to open using BASS', 'LoadPlayerFromFile');
    end;
  end else begin
    Log.LogError('Sound not found: "' + Name + '"', 'LoadPlayerFromFile');
    exit;
  end;
end;

//Equalizer
function TMusic.GetFFTData: TFFTData;
{var
Data: TFFTData;}
begin
  //Get Channel Data Mono and 256 Values
  BASS_ChannelGetData(Bass, @Result, BASS_DATA_FFT512);
  //Result := Data;
end;

function TMusic.LoadCustomSound(const Filename: String): Cardinal;
var
  S: hStream;
  I: Integer;
  F: String;
begin
  //Search for Sound in already loaded Sounds
  F := UpperCase(SoundPath + FileName);
  For I := 0 to High(CustomSounds) do
  begin
    if (UpperCase(CustomSounds[I].Filename) = F) then
    begin
      Result := I;
      Exit;
    end;
  end;

  if LoadSoundFromFile(S, SoundPath + Filename) then
    Result := High(CustomSounds)
  else
    Result := 0;
end;

procedure TMusic.PlayCustomSound(const Index: Cardinal);
begin
if Index <= High(CustomSounds) then
  BASS_ChannelPlay(CustomSounds[Index].Handle, True);
end;

procedure TMusic.EnableVocalRemover;
begin
  if DSP_VocalRemover=0 then
    DSP_VocalRemover := Bass_ChannelSetDSP(Bass, @SBVocalCut16, 0, 0);

end;

procedure TMusic.DisableVocalRemover;
begin
  if DSP_VocalRemover<>0 then
  begin
    BASS_ChannelRemoveDSP(Bass, DSP_VocalRemover);
    DSP_VocalRemover := 0;
  end;
end;

function TMusic.VocalRemoverActivated(): boolean;
begin
  Result := (DSP_VocalRemover <> 0);
end;


function TMusic.VoicesOpen(Names: array of string): integer;
var
  I:    integer;
  num:  integer;

begin
  SetLength(BassVoices, 0);

  for I := 0 to high(Names) do
  begin
    if FileExists(Names[I]) then
    begin
      num := high(BassVoices)+1;
      SetLength(BassVoices, num+1);
      BassVoices[num] := Bass_StreamCreateFile(false, pchar(Names[I]), 0, 0, 0);
      DSP_VocalRemover:=0;
    end;
  end;

  Result := high(BassVoices)+1;
end;

procedure TMusic.VoicesPlay;
var
  I:  integer;
begin
  for I := 0 to high(BassVoices) do
  begin
    BASS_ChannelPlay(BassVoices[I], True);
  end;
end;

procedure TMusic.VoicesStop;
var
  I:  integer;
begin
  for I := 0 to high(BassVoices) do
    Bass_ChannelStop(BassVoices[I]);
end;


procedure TMusic.VoicesClose;
var
  I:  integer;
begin
  for I := 0 to high(BassVoices) do
  begin
    Bass_StreamFree(BassVoices[I]);
    DSP_VocalRemover:=0;
  end;
  SetLength(BassVoices, 0);
end;
end.