{*************************************************************************}
{                                                                         }
{ GamePlayer.pas - Game Player Engine                                     }
{                                                                         }
{ This file is a part of the project:                                     }
{   Game Player - object-oriented sound engine based on BASS.DLL          }
{                                                                         }
{ © Parfenov Alexey Andreevich, 2007                                      }
{                                                                         }
{ e-mail: AlkatrazStudio@yandex.ru                                        }
{                                                                         }
{ This library is free software; you can redistribute it and/or           }
{ modify it under the terms of the GNU General Public License             }
{ as published by the Free Software Foundation; either version 2.1 of     }
{ the License, or (at your option) any later version.                     }
{                                                                         }
{ This library is distributed in the hope that it will be useful,         }
{ but WITHOUT ANY WARRANTY; without even the implied warranty of          }
{ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU        }
{ General Public License for more details.                                }
{                                                                         }
{ You may read GNU General Public License at:                             }
{   http://www.gnu.org/copyleft/gpl.html                                  }
{                                                                         }
{ Official BASS.DLL website: http://un4seen.com                           }
{                                                                         }
{ last modified: 25.04.2009                                               }
{*************************************************************************}
unit GamePlayer;

{$I gameplayer.inc}
{$IFDEF GP_PLUGINS}{$IFDEF GP_EFFECTS}{$IFDEF GP_PLAYLIST}
{$DEFINE GP_VISUAL}
{$ENDIF}{$ENDIF}{$ENDIF}

interface

uses Dynamic_Bass,microutils,windows,microwin,messages
{$IFDEF GP_PLUGINS},pluginctrl{$ENDIF}
;

const
  WM_GP=2000;
  WM_GP_PLAYEND=WM_GP;
  WM_GP_PLAYSTART=WM_GP+1;
  WM_GP_NEWTAGS=WM_GP+2;
  //
  GP_BASSEXTCOUNT=13;

{$IFDEF GP_EFFECTS}
type TGPFX_EAX=class
  private
    procedure SetDecay(const Value: FLOAT);
    procedure SetDamp(const Value: FLOAT);
    procedure SetEnabled(const Value: boolean);
    procedure SetEnvironment(const Value: Cardinal);
    procedure SetVolume(const Value: FLOAT);
  protected
    FEnvironment:Cardinal;
    FVolume,FDecay,FDamp:FLOAT;
    FEnabled:boolean;
    FSupportEAX:boolean;
    tmpVol:FLOAT;
  public
    constructor Create;
  //published
    property Enabled:boolean read FEnabled write SetEnabled;
    property SupportEAX:boolean read FSupportEAX;
    property Environment:Cardinal read FEnvironment write SetEnvironment;
    property Volume:FLOAT read FVolume write SetVolume;
    property Decay:FLOAT read FDecay write SetDecay;
    property Damp:FLOAT read FDamp write SetDamp;
end;

type TGPFX_Chorus=class
  private
    procedure SetDelay(const Value: FLOAT);
    procedure SetDepth(const Value: FLOAT);
    procedure SetFeedback(const Value: FLOAT);
    procedure SetFrequency(const Value: FLOAT);
    procedure SetPhase(const Value: DWORD);
    procedure SetWaveForm(const Value: DWORD);
    procedure SetWetDryMix(const Value: FLOAT);
  protected
    Handle:HFX;
    Music:HMUSIC;
    FX:BASS_DX8_CHORUS;
    OK:boolean;
  public
    constructor Create(MusicHandle:HMUSIC);
    destructor Destroy;override;
    procedure RestoreDefaults;
  //published
    property WetDryMix:FLOAT read FX.fWetDryMix write SetWetDryMix;
    property Depth:FLOAT read FX.fDepth write SetDepth;
    property Feedback:FLOAT read FX.fFeedback write SetFeedback;
    property Frequency:FLOAT read FX.fFrequency write SetFrequency;
    property WaveForm:DWORD read FX.lWaveForm write SetWaveForm;
    property Delay:FLOAT read FX.fDelay write SetDelay;
    property Phase:DWORD read FX.lPhase write SetPhase;
end;

type TGPFX_Compressor=class
  private
    procedure SetAttack(const Value: FLOAT);
    procedure SetGain(const Value: FLOAT);
    procedure SetPredelay(const Value: FLOAT);
    procedure SetRatio(const Value: FLOAT);
    procedure SetRelease(const Value: FLOAT);
    procedure SetThreshold(const Value: FLOAT);
  protected
    Handle:HFX;
    Music:Cardinal;
    FX:BASS_DX8_COMPRESSOR;
    OK:boolean;
  public
    constructor Create(MusicHandle:Cardinal);
    destructor Destroy;override;
    function RestoreDefaults:boolean;
  //published
    property Gain:FLOAT read FX.fGain write SetGain;
    property Attack:FLOAT read FX.fAttack write SetAttack;
    property Release:FLOAT read FX.fRelease write SetRelease;
    property Threshold:FLOAT read FX.fThreshold write SetThreshold;
    property Ratio:FLOAT read FX.fRatio write SetRatio;
    property Predelay:FLOAT read FX.fPredelay write SetPredelay;
end;

type TGPFX_Distortion=class
  private
    procedure SetEdge(const Value: FLOAT);
    procedure SetGain(const Value: FLOAT);
    procedure SetPostEQBandwidth(const Value: FLOAT);
    procedure SetPostEQCenterFrequency(const Value: FLOAT);
    procedure SetPreLowpassCutoff(const Value: FLOAT);
  protected
    Handle:HFX;
    Music:Cardinal;
    FX:BASS_DX8_DISTORTION;
    OK:boolean;
  public
    constructor Create(MusicHandle:Cardinal);
    destructor Destroy;override;
    function RestoreDefaults:boolean;
  //published
    property Gain:FLOAT read FX.fGain write SetGain;
    property Edge:FLOAT read FX.fEdge write SetEdge;
    property PostEQCenterFrequency:FLOAT read FX.fPostEQCenterFrequency write SetPostEQCenterFrequency;
    property PostEQBandwidth:FLOAT read FX.fPostEQBandwidth write SetPostEQBandwidth;
    property PreLowpassCutoff:FLOAT read FX.fPreLowpassCutoff write SetPreLowpassCutoff;
end;

type TGPFX_Echo=class
  private
    procedure SetFeedback(const Value: FLOAT);
    procedure SetLeftDelay(const Value: FLOAT);
    procedure SetPanDelay(const Value: BOOL);
    procedure SetRightDelay(const Value: FLOAT);
    procedure SetWetDryMix(const Value: FLOAT);
  protected
    Handle:HFX;
    Music:Cardinal;
    FX:BASS_DX8_ECHO;
    OK:boolean;
  public
    constructor Create(MusicHandle:Cardinal);
    destructor Destroy;override;
    function RestoreDefaults:boolean;
  //published
    property WetDryMix:FLOAT read FX.fWetDryMix write SetWetDryMix;
    property Feedback:FLOAT read FX.fFeedback write SetFeedback;
    property LeftDelay:FLOAT read FX.fLeftDelay write SetLeftDelay;
    property RightDelay:FLOAT read FX.fRightDelay write SetRightDelay;
    property PanDelay:BOOL read FX.lPanDelay write SetPanDelay;
end;

type TGPFX_Flanger=class
  private
    procedure SetDelay(const Value: FLOAT);
    procedure SetDepth(const Value: FLOAT);
    procedure SetFeedback(const Value: FLOAT);
    procedure SetFrequency(const Value: FLOAT);
    procedure SetPhase(const Value: DWORD);
    procedure SetWaveform(const Value: DWORD);
    procedure SetWetDryMix(const Value: FLOAT);
  protected
    Handle:HFX;
    Music:Cardinal;
    FX:BASS_DX8_FLANGER;
    OK:boolean;
  public
    constructor Create(MusicHandle:Cardinal);
    destructor Destroy;override;
    function RestoreDefaults:boolean;
  //published
    property WetDryMix:FLOAT read FX.fWetDryMix write SetWetDryMix;
    property Depth:FLOAT read FX.fDepth write SetDepth;
    property Feedback:FLOAT read FX.fFeedback write SetFeedback;
    property Frequency:FLOAT read FX.fFrequency write SetFrequency;
    property Waveform:DWORD read FX.lWaveform write SetWaveform;
    property Delay:FLOAT read FX.fDelay write SetDelay;
    property Phase:DWORD read FX.lPhase write SetPhase;
end;

type TGPFX_Gargle=class
  private
    procedure SetRateHZ(const Value: DWORD);
    procedure SetWaveShape(const Value: DWORD);
  protected
    Handle:HFX;
    Music:Cardinal;
    FX:BASS_DX8_GARGLE;
    OK:boolean;
  public
    constructor Create(MusicHandle:Cardinal);
    destructor Destroy;override;
    function RestoreDefaults:boolean;
  //published
    property RateHz:DWORD read FX.dwRateHZ write SetRateHZ;
    property WaveShape:DWORD read FX.dwWaveShape write SetWaveShape;
end;

type TGPFX_I3DL2Reverb=class
  private
    procedure SetDecayHFRatio(const Value: FLOAT);
    procedure SetDecayTime(const Value: FLOAT);
    procedure SetDensity(const Value: FLOAT);
    procedure SetDiffusion(const Value: FLOAT);
    procedure SetHFReference(const Value: FLOAT);
    procedure SetReflectionsDelay(const Value: FLOAT);
    procedure SetReflections(const Value: Integer);
    procedure SetReverb(const Value: Integer);
    procedure SetReverbDelay(const Value: FLOAT);
    procedure SetRoom(const Value: Integer);
    procedure SetRoomHF(const Value: Integer);
    procedure SetRoomRolloffFactor(const Value: FLOAT);
  protected
    Handle:HFX;
    Music:Cardinal;
    FX:BASS_DX8_I3DL2REVERB;
    OK:boolean;
  public
    constructor Create(MusicHandle:Cardinal);
    destructor Destroy;override;
    function RestoreDefaults:boolean;
  //published
    property Room:Integer read FX.lRoom write SetRoom;
    property RoomHF:Integer read FX.lRoomHF write SetRoomHF;
    property RoomRolloffFactor:FLOAT read FX.flRoomRolloffFactor write SetRoomRolloffFactor;
    property DecayTime:FLOAT read FX.flDecayTime write SetDecayTime;
    property DecayHFRatio:FLOAT read FX.flDecayHFRatio write SetDecayHFRatio;
    property Reflections:Integer read FX.lReflections write SetReflections;
    property ReflectionsDelay:FLOAT read FX.flReflectionsDelay write SetReflectionsDelay;
    property Reverb:Integer read FX.lReverb write SetReverb;
    property ReverbDelay:FLOAT read FX.flReverbDelay write SetReverbDelay;
    property Diffusion:FLOAT read FX.flDiffusion write SetDiffusion;
    property Density:FLOAT read FX.flDensity write SetDensity;
    property HFReference:FLOAT read FX.flHFReference write SetHFReference;
end;

type TGPFX_Reverb=class
  private
    procedure SetHighFreqRTRatio(const Value: FLOAT);
    procedure SetInGain(const Value: FLOAT);
    procedure SetReverbMix(const Value: FLOAT);
    procedure SetReverbTime(const Value: FLOAT);
  protected
    Handle:HFX;
    Music:Cardinal;
    FX:BASS_DX8_REVERB;
    OK:boolean;
  public
    constructor Create(MusicHandle:Cardinal);
    destructor Destroy;override;
    function RestoreDefaults:boolean;
  //published
    property InGain:FLOAT read FX.fInGain write SetInGain;
    property ReverbMix:FLOAT read FX.fReverbMix write SetReverbMix;
    property ReverbTime:FLOAT read FX.fReverbTime write SetReverbTime;
    property HighFreqRTRatio:FLOAT read FX.fHighFreqRTRatio write SetHighFreqRTRatio;
end;

type TGPFX_EQBands=array[0..8]of BASS_DX8_PARAMEQ;

type TGPFX_Equalizer=class
  private
    function GetBand(Index: Integer): FLOAT;
    procedure SetBand(Index: Integer; const Value: FLOAT);
  protected
    Handle:array[0..8]of HFX;
    Music:Cardinal;
    FX:TGPFX_EQBands;
    OK:boolean;
  public
    constructor Create(MusicHandle:Cardinal);
    destructor Destroy;override;
    function RestoreDefaults:boolean;
    property EQBand[Index:Integer]:FLOAT read GetBand write SetBand;
end;
{$ENDIF}

type TGPStore=record
{$IFDEF GP_EFFECTS}
  UseEAX:boolean;
{$ENDIF}
  Pan:Integer;
  Volume:integer;
{$IFDEF GP_EFFECTS}
  ChorusEnabled:boolean;
  CompressorEnabled:boolean;
  DistortionEnabled:boolean;
  EchoEnabled:boolean;
  FlangerEnabled:boolean;
  GargleEnabled:boolean;
  I3DL2ReverbEnabled:boolean;
  ReverbEnabled:boolean;
  EqualizerEnabled:boolean;
  Chorus:BASS_DX8_CHORUS;
  Compressor:BASS_DX8_COMPRESSOR;
  Distortion:BASS_DX8_DISTORTION;
  Echo:BASS_DX8_ECHO;
  Flanger:BASS_DX8_FLANGER;
  Gargle:BASS_DX8_GARGLE;
  I3DL2Reverb:BASS_DX8_I3DL2REVERB;
  Reverb:BASS_DX8_REVERB;
  Equalizer:TGPFX_EQBands;
{$ENDIF}
end;

{$IFDEF GP_EFFECTS}
type TGPEffects=class
  protected
    FMusic:Cardinal;
    FFailIfExist:boolean;
    FEffectsList:array of Pointer;
    FChorus:TGPFX_Chorus;
    FCompressor:TGPFX_Compressor;
    FDistortion:TGPFX_Distortion;
    FEcho:TGPFX_Echo;
    FFlanger:TGPFX_Flanger;
    FGargle:TGPFX_Gargle;
    FI3DL2Reverb:TGPFX_I3DL2Reverb;
    FReverb:TGPFX_Reverb;
    FEqualizer:TGPFX_Equalizer;
    FCreateAnyway:boolean;
    procedure ApplyChanges;
  public
    constructor Create(MusicHandle:Cardinal);
    destructor Destroy;override;
    function Add_Chorus:TGPFX_Chorus;
    procedure Remove_Chorus;
    function Add_Compressor:TGPFX_Compressor;
    procedure Remove_Compressor;
    function Add_Distortion:TGPFX_Distortion;
    procedure Remove_Distortion;
    function Add_Echo:TGPFX_Echo;
    procedure Remove_Echo;
    function Add_Flanger:TGPFX_Flanger;
    procedure Remove_Flanger;
    function Add_Gargle:TGPFX_Gargle;
    procedure Remove_Gargle;
    function Add_I3DL2Reverb:TGPFX_I3DL2Reverb;
    procedure Remove_I3DL2Reverb;
    function Add_Reverb:TGPFX_Reverb;
    procedure Remove_Reverb;
    function Add_Equalizer:TGPFX_Equalizer;
    procedure Remove_Equalizer;
    procedure SaveConfig(var x:TGPStore);
    procedure LoadConfig(var x:TGPStore);
  //published
    property Chorus:TGPFX_Chorus read FChorus;
    property Compressor:TGPFX_Compressor read FCompressor;
    property Distortion:TGPFX_Distortion read FDistortion;
    property Echo:TGPFX_Echo read FEcho;
    property Flanger:TGPFX_Flanger read FFlanger;
    property Gargle:TGPFX_Gargle read FGargle;
    property I3DL2Reverb:TGPFX_I3DL2Reverb read FI3DL2Reverb;
    property Reverb:TGPFX_Reverb read FReverb;
    property Equalizer:TGPFX_Equalizer read FEqualizer;
end;
{$ENDIF}

type TGPFFTData=array[0..31]of integer;

type TGPMusicType=
  (mtMusic,mtStream,{$IFDEF GP_PLUGINS}mtPlugin,{$ENDIF}{$IFDEF GP_INTERNET}mtURL,{$ENDIF}mtNone);

type TGPMusicState=(msIdle,msPaused,msPlaying);

type TGPOnPlayChange=procedure(Sender:TObject) of object;

type TGPMusic=class
  private
    function GetPosition: Integer;
    procedure SetPosition(const Value: Integer);
    {$IFDEF GP_EFFECTS}procedure SetUseEAX(const Value: boolean);{$ENDIF}
    procedure SetPan(const Value: integer);
    procedure SetVolume(const Value: integer);
    function GetLevel: DWORD;
  protected
    FisSound:boolean;
    FFileName:string;
    FMusicType:TGPMusicType;
    FMusicState:TGPMusicState;
    FnLoops:Word;
    FHandle:HMUSIC;
    FSample:HSAMPLE;
    FPan,FVolume:integer;
    FCanStore:boolean;
    FStoredConfig:TGPStore;
    {$IFDEF GP_EFFECTS}
    FEffects:TGPEffects;
    FUseEAX:boolean;
    {$ENDIF}
    FChannelLength:Cardinal;
    FTitle:string;
    FIndex:integer;
    FOnPlayEnd:TGPOnPlayChange;
    FOnPlayStart:TGPOnPlayChange;
    FTag:integer;
    FEndOffset:Cardinal;
    function DoPlay(Resume:boolean):boolean;
    function UpdateInfo:boolean;
    {$IFDEF GP_INTERNET}function UpdateInfoURL:boolean;{$ENDIF}
  public
    constructor Create;
    destructor Destroy;override;
    function Play(PlayCount:word=1):boolean;
    function Open(MusicFileName:string):boolean;
    function Pause:boolean;
    function UnPause:boolean;
    function TogglePause:boolean;
    function Stop:boolean;
    function Close:boolean;
    function Reload(KeepPosition:boolean):boolean;
    procedure SaveState(var store:TGPStore);
    procedure LoadState(var store:TGPStore);
    function GetFFTData(var data:TGPFFTData):boolean;
    property Handle:Cardinal read FHandle;
    property isSound:boolean read FisSound;
    property OnPlayEnd:TGPOnPlayChange read FOnPlayEnd write FOnPlayEnd;
    property OnPlayStart:TGPOnPlayChange read FOnPlayStart write FOnPlayStart;
  //published
    {$IFDEF GP_EFFECTS}property UseEAX:boolean read FUseEAX write SetUseEAX;{$ENDIF}
    property FileName:string read FFileName;
    property MusicType:TGPMusicType read FMusicType;
    property MusicState:TGPMusicState read FMusicState;
    property nLoops:Word read FnLoops write FnLoops;
    {$IFDEF GP_EFFECTS}property Effects:TGPEffects read FEffects;{$ENDIF}
    property Position:Integer read GetPosition write SetPosition;
    property Pan:integer read FPan write SetPan;
    property Volume:integer read FVolume write SetVolume;
    property ChannelLength:Cardinal read FChannelLength;
    property Title:string read FTitle;
    property Index:integer read FIndex;
    property Tag:integer read FTag write FTag;
    property Level:DWORD read GetLevel;
    property EndOffset:Cardinal read FEndOffset write FEndOffset;
end;

{$IFDEF GP_PLAYLIST}
type TGPPlayListMode=(pmDisabled,pmSequence,pmLoopAll,pmLoopOne,pmShuffle);

type TGPSortMethod=(smFileName,smTitle,smPath,smShuffle);

type TGPPlayList=class
  private
    function GetFiles(Index: integer): string;
    function GetTitles(Index: integer): string;
    function PlayOffset(Offset:integer):boolean;
    procedure SetMusicIndex(Value:Integer);
  protected
    FFiles:array of string;
    FTitles:array of string;
    FMode:TGPPlayListMode;
    FMusicIndex:integer;
    FCount:integer;
    FInfoOnLoad:boolean;
    FTempDisable:boolean;
  public
    constructor Create;
    destructor Destroy;override;
    function PlayNext:boolean;
    function PlayPrev:boolean;
    function PlayCurrent:boolean;
    property Files[Index:integer]:string read GetFiles;
    property Titles[Index:integer]:string read GetTitles;
    function AddFile(FileName:string):boolean;
    function AddFromFolder(Dir:string;SubDirs:boolean;DoClear:boolean=false):boolean;
    function Delete(Index:integer):integer;
    procedure Clear;
    procedure Exchange(Index1,Index2:integer);
    function PlayMusic(FileIndex:integer):boolean;
    procedure Sort(SortMethod:TGPSortMethod;Backwards:boolean);
    function LoadFromFile(FileName:string;DoClear:boolean=true):boolean;
    function SaveToFile(FileName:string):boolean;
  //published
    property Mode:TGPPlayListMode read FMode write FMode;
    property Count:integer read FCount;
    property MusicIndex:integer read FMusicIndex write SetMusicIndex;
    property InfoOnLoad:boolean read FInfoOnLoad write FInfoOnLoad;
    property TempDisable:boolean read FTempDisable;
end;
{$ENDIF}

{$IFDEF GP_PLUGINS}
type TGPPlugins=class
  private
    function GetPluginInfo(Index: Integer): TPluginInfo;
    function GetPlugin(Index: Integer): TPlugin;
  protected
    FPlugList:array of string;
    FHandList:array of Cardinal;
    nPlugins:integer;
    FPluginsOK:boolean;
    FWinampDir:string;
    FCurPlugin:TPlugin;
    FFilter:string;
  public
    constructor Create;
    destructor Destroy; override;
    property NumberPlugins:integer read nPlugins;
    property PluginsOK:boolean read FPluginsOK;
    property Plugins[Index:Integer]:TPlugin read GetPlugin;
    property PluginsInfo[Index:Integer]:TPluginInfo read GetPluginInfo;
    property CurrentPlugin:TPlugin read FCurPlugin;
    function LoadPlugin(FileName:string):boolean;
    procedure UnLoadPlugin(PluginNum:integer);
    function LoadPlugins(DirName:string):boolean;
    function LoadWinampPlugins:boolean;
    procedure UnLoadPlugins;
    procedure ShowConfigWindow(PluginNum:integer);
    procedure ShowAboutWindow(PluginNum:integer);
    function ActivatePlugin(FileName:string):boolean;
    function GetWinampPluginsDir:string;
end;
{$ENDIF}

type TGP_MessageCallBack=procedure(H,M,W,L:Longint);

type TGamePlayer=class
  private
    function GetMusic(Index: Integer): TGPMusic;
    function GetSupportedExtension(Index: Integer): string;
    function GetVolume: integer;
    procedure SetVolume(const Value: integer);
  protected
    FVerificationString:string;
    FMainWindow:HWND;
    FCallBackProc:TGP_MessageCallBack;
    FPlayerOK:boolean;
    FMusic:TGPMusic;
    {$IFDEF GP_EFFECTS}FEAX:TGPFX_EAX;{$ENDIF}
    FDeviceName:string;
    FDeviceDriver:string;
    FBassVer:string;
    FMusicSupport:string;
    FFilter:string;
    FSupportedExtensions:array of string;
    FNumberOfExtensions:integer;
    FVolume:Integer;
    FMusicsList:array of TGPMusic;
    {$IFDEF GP_PLAYLIST}FPlayList:TGPPlayList;{$ENDIF}
    {$IFDEF GP_PLUGINS}FPlugins:TGPPlugins;{$ENDIF}
    FTag:integer;
    FSupportDS9,FSupport32Float:boolean;
    FFlags,FSafeFlags:DWORD;
    FMusicCount:integer;
    function AddFX(FXType:Integer;MusicHandle:Cardinal;var FXHandle:Cardinal;FXData:Pointer):boolean;
    {$IFDEF GP_PLUGINS}procedure UpdateFilesFilter;{$ENDIF}
  public
    constructor Create(BassDir:string='');
    destructor Destroy;override;
    property SupportedExtensions[Index:Integer]:string read GetSupportedExtension;
    {$IFDEF GP_PLUGINS}function ConfigCurrentPlugin:boolean;{$ENDIF}
    function FastSound(FileName:string):boolean;
    property MainWindow:HWND read FMainWindow;
    property CallBackProc:TGP_MessageCallBack read FCallBackProc write FCallBackProc;
    property Musics[Index:Integer]:TGPMusic read GetMusic;
    procedure Stop;
    {$IFDEF GP_INTERNET}function IsSupportedURL(url:string):boolean;{$ENDIF}
  //published
    property PlayerOK:boolean read FPlayerOK;
    {$IFDEF GP_EFFECTS}property EAX:TGPFX_EAX read FEAX;{$ENDIF}
    property Music:TGPMusic read FMusic;
    property SupportDS9:boolean read FSupportDS9;
    property Support32Float:boolean read FSupport32Float;
    property DeviceName:string read FDeviceName;
    property DeviceDriver:string read FDeviceDriver;
    property BassVer:string read FBassVer;
    property MusicSupport:string read FMusicSupport;
    property Filter:string read FFilter;
    property NumberOfExtensions:integer read FNumberOfExtensions;
    property Volume:integer read GetVolume write SetVolume;
    {$IFDEF GP_PLAYLIST}property PlayList:TGPPlayList read FPlayList;{$ENDIF}
    {$IFDEF GP_PLUGINS}property Plugins:TGPPlugins read FPlugins;{$ENDIF}
    property Tag:integer read FTag write FTag;
    property MusicCount:integer read FMusicCount;
end;

function GetGamePlayer:TGamePlayer;
procedure GetGPDefaultStore(var Store:TGPStore);

procedure PlayEnd(Music:TGPMusic);

implementation

const VerificationString='We break the silence';

var Player:TGamePlayer=nil;

var DefaultStore:TGPStore;

function GetGamePlayer:TGamePlayer;
begin
  result:=Player;
end;

{$IFDEF GP_EFFECTS}
procedure GetGPDefaultStore(var Store:TGPStore);
begin
  Store:=DefaultStore;
end;
{$ENDIF}

procedure PlayEnd(Music:TGPMusic);
begin
  if Assigned(Music.FOnPlayEnd) then Music.FOnPlayEnd(Music);
  SendMessage(Player.FMainWindow,WM_GP_PLAYEND,
  {$IFDEF GP_PLAYLIST}Player.PlayList.MusicIndex{$ELSE}-1{$ENDIF},Music.FTag);
  //Music.Stop;
  {$IFDEF GP_PLAYLIST}
  if(not Music.FisSound)and(not Player.PlayList.TempDisable)then
  begin
    if Player.PlayList.Mode<>pmDisabled then
      Player.PlayList.PlayNext;
  end
  else
  begin
  {$ENDIF}
    if Music.FnLoops>0 then
    begin
      Music.FnLoops:=Music.FnLoops-1;
      if Music.FnLoops>0 then Music.DoPlay(false);
    end else Music.DoPlay(false);
  {$IFDEF GP_PLAYLIST}end;{$ENDIF}
end;

procedure MusicSyncProc(handle:HSYNC;channel,data:DWORD;user:Pointer);stdcall;
begin
  PlayEnd(TGPMusic(user));
end;

{$IFDEF GP_INTERNET}
procedure MusicSyncProcTags(handle:HSYNC;channel,data:DWORD;user:Pointer);stdcall;
begin
  TGPMusic(user).UpdateInfoURL;
  if Player.MainWindow<>0 then
    SendMessage(Player.MainWindow,WM_GP_NEWTAGS,TGPMusic(user).Index,0);
end;
{$ENDIF}

function GPWndProc(H,M,W,L:longint):longint;stdcall;
begin
  {$IFDEF GP_PLUGINS}
    pluginctrl.ProcMessage(H,M,W,L);
  {$ENDIF}
  if @Player.FCallBackProc<>nil then player.FCallBackProc(H,M,W,L);
  result:=DefWindowProc(H,M,W,L);
end;

{ TGamePlayer }

function TGamePlayer.AddFX(FXType: Integer; MusicHandle: Cardinal;
  var FXHandle: Cardinal; FXData: Pointer): boolean;
begin
  FXHandle:=BASS_ChannelSetFX(MusicHandle,FXType,1);
  result:=(FXhandle<>0);
  if result then
    BASS_FXGetParameters(FXHandle,FXData);
end;

{$IFDEF GP_PLUGINS}
function TGamePlayer.ConfigCurrentPlugin: boolean;
begin
  result:=(FPlugins.FCurPlugin<>nil);
  if result then FPlugins.FCurPlugin.config(FMainWindow);
end;
{$ENDIF}

constructor TGamePlayer.Create(BassDir:string='');
var
  v:Cardinal;

  function BassOK(DLLDir:string):boolean;
  var
    s:string;
    n:integer;
  begin
    s:=DLLDir;n:=length(s);if(n>0)then if s[n]<>'\'then s:=s+'\';s:=s+'Bass.dll';
    result:=FileExist(s);if not result then exit;
    result:=Load_BASSDLL(s);if not result then exit;
    v:=BASS_GetVersion;
    result:=(HIWORD(v) = BASSVERSION);
    if not result then Unload_BASSDLL;
  end;

var
  i:BASS_INFO;
  d:DWORD;
  devinfo:BASS_DEVICEINFO;
  {$IFDEF GP_EFFECTS}tmp:cardinal;{$ENDIF}

  {$IFDEF GP_EFFECTS}
  procedure TestEffect(TheType:Cardinal;TheData:Pointer;var TheBool:boolean);
  var
    fx:cardinal;
  begin
    fx:=BASS_ChannelSetFX(tmp,TheType,1);
    if fx=0 then begin TheBool:=false;exit;end;
    TheBool:=BASS_FXGetParameters(fx,TheData);
    if TheBool=false then
      BASS_ChannelRemoveFX(tmp,fx)
    else
      TheBool:=BASS_ChannelRemoveFX(tmp,fx);
  end;
  {$ENDIF}
  
begin
  if Player<>nil then exit;
  inherited Create;
  FVerificationString:=VerificationString;
  Player:=self;
  FPlayerOK:=BassOK(BassDir);
  if not FPlayerOK then
  begin
    FPlayerOK:=BassOK(GetWorkDir);
    if not FPlayerOK then
    begin
      FPlayerOK:=BassOK(GetWindowsDir);
      if not FPlayerOK then
        FPlayerOK:=BassOK(GetSystemDir);
    end;
  end;
  if not FPlayerOK then exit;
  FPlayerOK:=BASS_Init(1,44100,BASS_DEVICE_3D or BASS_DEVICE_LATENCY or BASS_DEVICE_NOSPEAKER,FMainWindow,nil);
  if not FPlayerOK then
    FPlayerOK:=BASS_Init(1,44100,0,FMainWindow,nil);
  if not FPlayerOK then begin Unload_BASSDLL;exit;end;
  FPlayerOK:=BASS_GetDeviceInfo(1,devinfo);
  if FPlayerOK then
    begin FDeviceName:=devinfo.name;FDeviceDriver:=devinfo.driver;end
  else
    begin Unload_BASSDLL;exit;end;
  BASS_GetInfo(i);FSupportDS9:=(i.dsver>=9);
  d:=BASS_StreamCreate(44100,BASS_SAMPLE_FLOAT,0,nil,nil);
  FSupport32Float:=(d<>0);if FSupport32Float then BASS_StreamFree(d);
  FFlags:=0;FSafeFlags:=BASS_SAMPLE_FX;
  if not FSupportDS9 then FFlags:=FFlags or BASS_SAMPLE_FX;
  BASS_SetConfig(BASS_CONFIG_3DALGORITHM,BASS_3DALG_OFF);
  FMusic:=TGPMusic.Create;FMusic.FisSound:=false;
  FBassVer:=inttostr(HIBYTE(HIWORD(v)))+'.'+
            inttostr(LOBYTE(HIWORD(v)))+'.'+
            inttostr(HIBYTE(LOWORD(v)))+'.'+
            inttostr(LOBYTE(LOWORD(v)));
  {$IFDEF GP_EFFECTS}
  FEAX:=TGPFX_EAX.Create;
  FEAX.Enabled:=true;
  tmp:=BASS_StreamCreate(44100,2,FFlags,nil,nil);
  if tmp=0 then begin FPlayerOK:=false;Unload_BASSDLL;exit;end;
  ZeroMemory(@DefaultStore,sizeof(DefaultStore));
  DefaultStore.UseEAX:=FEAX.SupportEAX;
  DefaultStore.Volume:=100;
  TestEffect(BASS_FX_DX8_CHORUS,@DefaultStore.Chorus,DefaultStore.ChorusEnabled);
  TestEffect(BASS_FX_DX8_COMPRESSOR,@DefaultStore.Compressor,DefaultStore.CompressorEnabled);
  TestEffect(BASS_FX_DX8_DISTORTION,@DefaultStore.Distortion,DefaultStore.DistortionEnabled);
  TestEffect(BASS_FX_DX8_ECHO,@DefaultStore.Echo,DefaultStore.EchoEnabled);
  TestEffect(BASS_FX_DX8_FLANGER,@DefaultStore.Flanger,DefaultStore.FlangerEnabled);
  TestEffect(BASS_FX_DX8_GARGLE,@DefaultStore.Gargle,DefaultStore.GargleEnabled);
  TestEffect(BASS_FX_DX8_I3DL2REVERB,@DefaultStore.I3DL2Reverb,DefaultStore.I3DL2ReverbEnabled);
  TestEffect(BASS_FX_DX8_REVERB,@DefaultStore.Reverb,DefaultStore.ReverbEnabled);
  DefaultStore.EqualizerEnabled:=true;
  if BASS_StreamFree(tmp)=false then begin FPlayerOK:=false;Unload_BASSDLL;end;
  {$ENDIF}
  {$IFDEF GP_PLUGINS}
  FPlugins:=TGPPlugins.Create;
  UpdateFilesFilter;
  {$ENDIF}
  {$IFDEF GP_PLAYLIST}FPlayList:=TGPPlayList.Create;{$ENDIF}
  //
  FMainWindow:=MakeWindow('TGamePlayerWindow',@GPWndProc);
  Randomize;
end;

destructor TGamePlayer.destroy;
begin
  FreeAndNil(FMusic);
  {$IFDEF GP_PLAYLIST}FreeAndNil(FPlayList);{$ENDIF}
  {$IFDEF GP_PLUGINS}FreeAndNil(FPlugins);{$ENDIF}
  Unload_BASSDLL;
  Player:=nil;
  inherited;
end;

function TGamePlayer.FastSound(FileName: string): boolean;
var
  h:HCHANNEL;
begin
  result:=false;
  h:=BASS_StreamCreateFile(false,PAnsiChar(FileName),0,0,BASS_STREAM_AUTOFREE);
  if h=0 then
    h:=BASS_MusicLoad(false,PAnsiChar(FileName),0,0,BASS_MUSIC_STOPBACK or BASS_MUSIC_AUTOFREE,0);
  if h=0 then exit;
  result:=BASS_ChannelPlay(h,true);  
end;

function TGamePlayer.GetMusic(Index: Integer): TGPMusic;
begin
  result:=FMusicsList[Index];
end;

function TGamePlayer.GetSupportedExtension(Index: Integer): string;
begin
  result:=FsupportedExtensions[Index];
end;

function TGamePlayer.GetVolume: integer;
begin
  result:=trunc(100*BASS_GetVolume);
end;

{$IFDEF GP_INTERNET}
function TGamePlayer.IsSupportedURL(url: string): boolean;
begin
  if length(url)>7 then
    result:=(UpperCase(copy(url,1,7))='HTTP://')or(UpperCase(copy(url,1,6))='FTP://')
  else
    result:=false;
end;
{$ENDIF}

procedure TGamePlayer.SetVolume(const Value: integer);
begin
  BASS_SetVolume(Value/100);
end;

procedure TGamePlayer.Stop;
var
  a:integer;
begin
  for a:=0 to FMusicCount-1 do
    Musics[a].Stop;
end;

{$IFDEF GP_PLUGINS}
procedure TGamePlayer.UpdateFilesFilter;
var
  s,all,ext,desc:string;
  a,x:integer;
  i:TPluginInfo;

procedure AddFilter;
var
  e:string;
  n1,b:integer;

procedure AddNewExt;
begin
  inc(x);Setlength(FSupportedExtensions,x+1);
  FSupportedExtensions[x]:='.';
end;

begin
  AddNewExt;
  n1:=length(ext);e:='*.';
  if n1>0 then
    for b:=1 to n1 do
      if ext[b]=';' then
      begin
        AddNewExt;
        e:=e+';*.';
      end
      else
      begin
        FSupportedExtensions[x]:=FSupportedExtensions[x]+ext[b];
        e:=e+ext[b];
      end;
  s:=s+desc+'|'+e+'|';
  all:=all+e+';';
end;

begin
  all:='';s:='';x:=-1;
  ext:='mp3;mp2;mp1;ogg;wav;aiff';desc:='Stream formats Bass.dll';
  AddFilter;
  ext:='mo3;it;xm;s3m;mtm;mod;umx';desc:='Module formats Bass.dll';
  AddFilter;
  if FPlugins.nPlugins>0 then
    for a:=0 to FPlugins.nPlugins-1 do
    begin
      i:=plugininfo[a];
      ext:=i.FileExtensions;desc:='Files, supported by '+ExtractFileName(i.Name);
      AddFilter;
    end;
  setlength(all,length(all)-1);
  FFilter:='All supported types|'+all+'|'+s+'All files|*';
  FNumberOfExtensions:=length(FSupportedExtensions);
end;
{$ENDIF}

{$IFDEF GP_EFFECTS}
{ TGPEffects }

function TGPEffects.Add_Chorus: TGPFX_Chorus;
begin
  if FChorus<>nil then if FFailIfExist then begin result:=nil;exit;end;
  result:=TGPFX_Chorus.Create(FMusic);
  if not Result.OK then begin if not FCreateAnyway then
  result.Free;result:=nil;end else FChorus:=result;
end;

function TGPEffects.Add_Compressor: TGPFX_Compressor;
begin
  if FCompressor<>nil then if FFailIfExist then begin result:=nil;exit;end;
  result:=TGPFX_Compressor.Create(FMusic);
  if not Result.OK then begin if not FCreateAnyway then
  result.Free;result:=nil;end else FCompressor:=result;
end;

function TGPEffects.Add_Distortion: TGPFX_Distortion;
begin
  if FDistortion<>nil then if FFailIfExist then begin result:=nil;exit;end;
  result:=TGPFX_Distortion.Create(FMusic);
  if not Result.OK then begin if not FCreateAnyway then
  result.Free;result:=nil;end else FDistortion:=result;
end;

function TGPEffects.Add_Echo: TGPFX_Echo;
begin
  if FEcho<>nil then if FFailIfExist then begin result:=nil;exit;end;
  result:=TGPFX_Echo.Create(FMusic);
  if not Result.OK then begin if not FCreateAnyway then
  result.Free;result:=nil;end else FEcho:=result;
end;

function TGPEffects.Add_Equalizer: TGPFX_Equalizer;
begin
  if FEqualizer<>nil then if FFailIfExist then begin result:=nil;exit;end;
  result:=TGPFX_Equalizer.Create(FMusic);
  if not Result.OK then begin if not FCreateAnyway then
  result.Free;result:=nil;end else FEqualizer:=result;
end;

function TGPEffects.Add_Flanger: TGPFX_Flanger;
begin
  if FFlanger<>nil then if FFailIfExist then begin result:=nil;exit;end;
  result:=TGPFX_Flanger.Create(FMusic);
  if not Result.OK then begin if not FCreateAnyway then
  result.Free;result:=nil;end else FFlanger:=result;
end;

function TGPEffects.Add_Gargle: TGPFX_Gargle;
begin
  if FGargle<>nil then if FFailIfExist then begin result:=nil;exit;end;
  result:=TGPFX_Gargle.Create(FMusic);
  if not Result.OK then begin if not FCreateAnyway then
  result.Free;result:=nil;end else FGargle:=result;
end;

function TGPEffects.Add_I3DL2Reverb: TGPFX_I3DL2Reverb;
begin
  if FI3DL2Reverb<>nil then if FFailIfExist then begin result:=nil;exit;end;
  result:=TGPFX_I3DL2Reverb.Create(FMusic);
  if not Result.OK then begin if not FCreateAnyway then
  result.Free;result:=nil;end else FI3DL2Reverb:=result;
end;

function TGPEffects.Add_Reverb: TGPFX_Reverb;
begin
  if FReverb<>nil then if FFailIfExist then begin result:=nil;exit;end;
  result:=TGPFX_Reverb.Create(FMusic);
  if not Result.OK then begin if not FCreateAnyway then
  result.Free;result:=nil;end else FReverb:=result;
end;

procedure TGPEffects.ApplyChanges;
var
  a:integer;
begin
  if FChorus<>nil then
    BASS_FXSetParameters(FChorus.Handle,@FChorus.FX);
  if FCompressor<>nil then
    BASS_FXSetParameters(FCompressor.Handle,@FCompressor.FX);
  if FDistortion<>nil then
    BASS_FXSetParameters(FDistortion.Handle,@FDistortion.FX);
  if FEcho<>nil then
    BASS_FXSetParameters(FEcho.Handle,@FEcho.FX);
  if FFlanger<>nil then
    BASS_FXSetParameters(FFlanger.Handle,@FFlanger.FX);
  if FGargle<>nil then
    BASS_FXSetParameters(FGargle.Handle,@FGargle.FX);
  if FI3DL2Reverb<>nil then
    BASS_FXSetParameters(FI3DL2Reverb.Handle,@FI3DL2Reverb.FX);
  if FReverb<>nil then
    BASS_FXSetParameters(FReverb.Handle,@FReverb.FX);
  if FEqualizer<>nil then
    for a:=0 to 8 do
      BASS_FXSetParameters(FEqualizer.Handle[a],@FEqualizer.FX[a]);
end;

constructor TGPEffects.Create(MusicHandle:Cardinal);
begin
  inherited Create;
  FMusic:=MusicHandle;SetLength(FEffectsList,0);
  FChorus:=nil;FCompressor:=nil;FDistortion:=nil;FEcho:=nil;
  FFlanger:=nil;FGargle:=nil;FI3DL2Reverb:=nil;FReverb:=nil;
  FEqualizer:=nil;
  FFailIfExist:=true;
end;

destructor TGPEffects.Destroy;
begin
  Remove_Chorus;Remove_Compressor;Remove_Distortion;Remove_Echo;
  Remove_Flanger;Remove_Gargle;Remove_I3DL2Reverb;Remove_Reverb;
  Remove_Equalizer;
  inherited;
end;

procedure TGPEffects.LoadConfig(var x: TGPStore);
begin
  FCreateAnyway:=true;
  if x.ChorusEnabled then begin Add_Chorus; if FChorus<>nil then FChorus.FX:=x.Chorus;end else Remove_Chorus;
  if x.CompressorEnabled then begin Add_Compressor;if FCompressor<>nil then FCompressor.FX:=x.Compressor;end else Remove_Compressor;
  if x.DistortionEnabled then begin Add_Distortion;if FDistortion<>nil then FDistortion.FX:=x.Distortion;end else Remove_Distortion;
  if x.EchoEnabled then begin Add_Echo;if FEcho<>nil then FEcho.FX:=x.Echo;end else Remove_Echo;
  if x.FlangerEnabled then begin Add_Flanger;if FFlanger<>nil then FFlanger.FX:=x.Flanger;end else Remove_Flanger;
  if x.GargleEnabled then begin Add_Gargle;if FGargle<>nil then FGargle.FX:=x.Gargle;end else Remove_Gargle;
  if x.I3DL2ReverbEnabled then begin Add_I3DL2Reverb;if FI3DL2Reverb<>nil then FI3DL2Reverb.FX:=x.I3DL2Reverb;end else Remove_I3DL2Reverb;
  if x.ReverbEnabled then begin Add_Reverb;if FReverb<>nil then FReverb.FX:=x.Reverb;end else Remove_Reverb;
  if x.EqualizerEnabled then begin Add_Equalizer;if FEqualizer<>nil then FEqualizer.FX:=x.Equalizer;end else Remove_Equalizer;
  ApplyChanges;
  FCreateAnyway:=false;
end;

procedure TGPEffects.Remove_Chorus;
begin
  if FChorus<>nil then FreeAndNil(FChorus);
end;

procedure TGPEffects.Remove_Compressor;
begin
  if FCompressor<>nil then FreeAndNil(FCompressor);
end;

procedure TGPEffects.Remove_Distortion;
begin
  if FDistortion<>nil then FreeAndNil(FDistortion);
end;

procedure TGPEffects.Remove_Echo;
begin
  if FEcho<>nil then FreeAndNil(FEcho);
end;

procedure TGPEffects.Remove_Equalizer;
begin
  if FEqualizer<>nil then FreeAndNil(FEqualizer);
end;

procedure TGPEffects.Remove_Flanger;
begin
  if FFlanger<>nil then FreeAndNil(FFlanger);
end;

procedure TGPEffects.Remove_Gargle;
begin
  if FGargle<>nil then FreeAndNil(FGargle);
end;

procedure TGPEffects.Remove_I3DL2Reverb;
begin
  if FI3DL2Reverb<>nil then FreeAndNil(FI3DL2Reverb);
end;

procedure TGPEffects.Remove_Reverb;
begin
  if FReverb<>nil then FreeAndNil(FReverb);
end;

procedure TGPEffects.SaveConfig(var x: TGPStore);
begin
  if FChorus<>nil then begin x.ChorusEnabled:=true;x.Chorus:=FChorus.FX;end else x.ChorusEnabled:=false;
  if FCompressor<>nil then begin x.CompressorEnabled:=true;x.Compressor:=FCompressor.FX;end else x.CompressorEnabled:=false;
  if FDistortion<>nil then begin x.DistortionEnabled:=true;x.Distortion:=FDistortion.FX;end else x.DistortionEnabled:=false;
  if FEcho<>nil then begin x.EchoEnabled:=true;x.Echo:=FEcho.FX;end else x.EchoEnabled:=false;
  if FFlanger<>nil then begin x.FlangerEnabled:=true;x.Flanger:=FFlanger.FX;end else x.FlangerEnabled:=false;
  if FGargle<>nil then begin x.GargleEnabled:=true;x.Gargle:=FGargle.FX;end else x.GargleEnabled:=false;
  if FI3DL2Reverb<>nil then begin x.I3DL2ReverbEnabled:=true;x.I3DL2Reverb:=FI3DL2Reverb.FX;end else x.I3DL2ReverbEnabled:=false;
  if FReverb<>nil then begin x.ReverbEnabled:=true;x.Reverb:=FReverb.FX;end else x.ReverbEnabled:=false;
  if FEqualizer<>nil then begin x.EqualizerEnabled:=true;x.Equalizer:=FEqualizer.FX;end else x.EqualizerEnabled:=false;
end;

{ TGPFX_EAX }

constructor TGPFX_EAX.Create;
begin
  inherited;
  FSupportEAX:=BASS_GetEAXParameters(FEnvironment,FVolume,FDecay,FDamp);
  FEnabled:=false;
end;

procedure TGPFX_EAX.SetDecay(const Value: FLOAT);
begin
  if(Value>=0.1)and(Value<=20)then
  begin
    FDecay := Value;
    BASS_SetEAXParameters(-1,-1,FDecay,-1);
  end;
end;

procedure TGPFX_EAX.SetDamp(const Value: FLOAT);
begin
  if(Value>=0)and(Value<=2)then
  begin
    FDamp := Value;
    BASS_SetEAXParameters(-1,-1,-1,FDamp);
  end;
end;

procedure TGPFX_EAX.SetEnabled(const Value: boolean);
begin
  if FSupportEAX then
    if FEnabled<>Value then
    begin
      FEnabled := Value;
      if FEnabled then BASS_SetEAXParameters(-1,tmpVol,-1,-1)
      else BASS_SetEAXParameters(-1,0,-1,-1);
    end;
end;

procedure TGPFX_EAX.SetEnvironment(const Value: Cardinal);
begin
  if Value<EAX_ENVIRONMENT_COUNT then
  begin
    FEnvironment := Value;
    BASS_SetEAXParameters(FEnvironment,-1,-1,-1);
  end;
end;

procedure TGPFX_EAX.SetVolume(const Value: FLOAT);
begin
  if(Value>=0)and(Value<=1)then
  begin
    FVolume := Value;
    if FEnabled then
      BASS_SetEAXParameters(-1,FVolume,-1,-1);
    tmpVol:=FVolume;
  end;
end;

{ TGPFX_Chorus }

constructor TGPFX_Chorus.Create(MusicHandle: HMUSIC);
begin
  Music:=MusicHandle;
  OK:=Player.AddFX(BASS_FX_DX8_CHORUS,Music,Handle,@fx);
end;

destructor TGPFX_Chorus.Destroy;
begin
  BASS_ChannelRemoveFX(Music,Handle);
  inherited;
end;

procedure TGPFX_Chorus.RestoreDefaults;
begin
  fx:=DefaultStore.Chorus;
  BASS_FXSetParameters(Handle,@FX);
end;

procedure TGPFX_Chorus.SetDelay(const Value: FLOAT);
begin
  if(Value>=0)and(Value<=20) then
  begin
    FX.fDelay := Value;
    BASS_FXSetParameters(Handle,@FX);
  end;
end;

procedure TGPFX_Chorus.SetDepth(const Value: FLOAT);
begin
  if(Value>=0)and(Value<=100) then
  begin
    FX.fDepth := Value;
    BASS_FXSetParameters(Handle,@FX);
  end;
end;

procedure TGPFX_Chorus.SetFeedback(const Value: FLOAT);
begin
  if(Value>=-99)and(Value<=99) then
  begin
    FX.fFeedback := Value;
    BASS_FXSetParameters(Handle,@FX);
  end;
end;

procedure TGPFX_Chorus.SetFrequency(const Value: FLOAT);
begin
  if(Value>=0)and(Value<=10) then
  begin
    FX.fFrequency := Value;
    BASS_FXSetParameters(Handle,@FX);
  end;
end;

procedure TGPFX_Chorus.SetPhase(const Value: DWORD);
begin
  if(Value<=4) then
  begin
    FX.lPhase := Value;
    BASS_FXSetParameters(Handle,@FX);
  end;
end;

procedure TGPFX_Chorus.SetWaveForm(const Value: DWORD);
begin
  if(Value<=1) then
  begin
    FX.lWaveform := Value;
    BASS_FXSetParameters(Handle,@FX);
  end;
end;

procedure TGPFX_Chorus.SetWetDryMix(const Value: FLOAT);
begin
  if(Value>=0)and(Value<=100) then
  begin
    FX.fWetDryMix := Value;
    BASS_FXSetParameters(Handle,@FX);
  end;
end;

{ TGPFX_Compressor }

constructor TGPFX_Compressor.Create(MusicHandle: Cardinal);
begin
  inherited Create;
  Music:=MusicHandle;
  ok:=player.AddFX(BASS_FX_DX8_COMPRESSOR,Music,Handle,@FX);
end;

destructor TGPFX_Compressor.Destroy;
begin
  BASS_ChannelRemoveFX(Music,Handle);
  inherited;
end;

function TGPFX_Compressor.RestoreDefaults: boolean;
begin
  FX:=DefaultStore.Compressor;
  result:=BASS_FXSetParameters(Handle,@FX);
end;

procedure TGPFX_Compressor.SetAttack(const Value: FLOAT);
begin
  if(Value>=0.01)and(Value<=500)then
  begin
    FX.fAttack:= Value;
    BASS_FXSetParameters(Handle,@FX);
  end;
end;

procedure TGPFX_Compressor.SetGain(const Value: FLOAT);
begin
  if(Value>=-60)and(Value<=60)then
  begin
    FX.fGain:= Value;
    BASS_FXSetParameters(Handle,@FX);
  end;
end;

procedure TGPFX_Compressor.SetPredelay(const Value: FLOAT);
begin
  if(Value>=0)and(Value<=4)then
  begin
    FX.fPredelay:= Value;
    BASS_FXSetParameters(Handle,@FX);
  end;
end;

procedure TGPFX_Compressor.SetRatio(const Value: FLOAT);
begin
  if(Value>=1)and(Value<=100)then
  begin
    FX.fRatio:= Value;
    BASS_FXSetParameters(Handle,@FX);
  end;
end;

procedure TGPFX_Compressor.SetRelease(const Value: FLOAT);
begin
  if(Value>=50)and(Value<=3000)then
  begin
    FX.fRelease:= Value;
    BASS_FXSetParameters(Handle,@FX);
  end;
end;

procedure TGPFX_Compressor.SetThreshold(const Value: FLOAT);
begin
  if(Value>=-60)and(Value<=0)then
  begin
    FX.fThreshold:= Value;
    BASS_FXSetParameters(Handle,@FX);
  end;
end;

{ TGPFX_Distortion }

constructor TGPFX_Distortion.Create(Musichandle: Cardinal);
begin
  inherited Create;
  Music:=Musichandle;
  ok:=Player.AddFX(BASS_FX_DX8_DISTORTION,Music,Handle,@FX);
end;

destructor TGPFX_Distortion.Destroy;
begin
  BASS_ChannelRemoveFX(Music,Handle);
  inherited;
end;

function TGPFX_Distortion.RestoreDefaults: boolean;
begin
  FX:=DefaultStore.Distortion;
  result:=BASS_FXSetParameters(Handle,@FX);
end;

procedure TGPFX_Distortion.SetEdge(const Value: FLOAT);
begin
  if(Value>=0)and(Value<=100)then
  begin
    FX.fEdge:=Value;
    BASS_FXSetParameters(Handle,@FX);
  end;
end;

procedure TGPFX_Distortion.SetGain(const Value: FLOAT);
begin
  if(Value>=-60)and(Value<=0)then
  begin
    FX.fGain:=Value;
    BASS_FXSetParameters(Handle,@FX);
  end;
end;

procedure TGPFX_Distortion.SetPostEQBandwidth(const Value: FLOAT);
begin
  if(Value>=100)and(Value<=8000)then
  begin
    FX.fPostEQBandwidth:=Value;
    BASS_FXSetParameters(Handle,@FX);
  end;
end;

procedure TGPFX_Distortion.SetPostEQCenterFrequency(const Value: FLOAT);
begin
  if(Value>=100)and(Value<=8000)then
  begin
    FX.fPostEQCenterFrequency:=Value;
    BASS_FXSetParameters(Handle,@FX);
  end;
end;

procedure TGPFX_Distortion.SetPreLowpassCutoff(const Value: FLOAT);
begin
  if(Value>=100)and(Value<=8000)then
  begin
    FX.fPreLowpassCutoff:=Value;
    BASS_FXSetParameters(Handle,@FX);
  end;
end;

{ TGPFX_Echo }

constructor TGPFX_Echo.Create(Musichandle: Cardinal);
begin
  inherited Create;
  Music:=Musichandle;
  ok:=Player.AddFX(BASS_FX_DX8_ECHO,Music,handle,@FX);
end;

destructor TGPFX_Echo.Destroy;
begin
  BASS_ChannelRemoveFX(Music,handle);
  inherited;
end;

function TGPFX_Echo.RestoreDefaults: boolean;
begin
  FX:=DefaultStore.Echo;
  result:=BASS_FXSetParameters(Handle,@FX);
end;

procedure TGPFX_Echo.SetFeedback(const Value: FLOAT);
begin
  if(Value>=0)and(Value<=100)then
  begin
    FX.fFeedback:=Value;
    BASS_FXSetParameters(Handle,@FX);
  end;
end;

procedure TGPFX_Echo.SetLeftDelay(const Value: FLOAT);
begin
  if(Value>=1)and(Value<=2000)then
  begin
    FX.fLeftDelay:=Value;
    BASS_FXSetParameters(Handle,@FX);
  end;
end;

procedure TGPFX_Echo.SetPanDelay(const Value: Bool);
begin
  FX.lPanDelay:=Value;
  BASS_FXSetParameters(Handle,@FX);
end;

procedure TGPFX_Echo.SetRightDelay(const Value: FLOAT);
begin
  if(Value>=1)and(Value<=2000)then
  begin
    FX.fRightDelay:=Value;
    BASS_FXSetParameters(Handle,@FX);
  end;
end;

procedure TGPFX_Echo.SetWetDryMix(const Value: FLOAT);
begin
  if(Value>=0)and(Value<=100)then
  begin
    FX.fWetDryMix:=Value;
    BASS_FXSetParameters(Handle,@FX);
  end;
end;

{ TGPFX_Flanger }

constructor TGPFX_Flanger.Create(Musichandle: Cardinal);
begin
  inherited Create;
  Music:=Musichandle;
  ok:=player.AddFX(BASS_FX_DX8_FLANGER,Music,Handle,@FX);
end;

destructor TGPFX_Flanger.Destroy;
begin
  BASS_ChannelRemoveFX(Music,Handle);
  inherited;
end;

function TGPFX_Flanger.RestoreDefaults: boolean;
begin
  FX:=DefaultStore.Flanger;
  result:=BASS_FXSetParameters(Handle,@FX);
end;

procedure TGPFX_Flanger.SetDelay(const Value: FLOAT);
begin
  if(Value>=0)and(Value<=4)then
  begin
    FX.fDelay:=Value;
    BASS_FXSetParameters(Handle,@FX);
  end;
end;

procedure TGPFX_Flanger.SetDepth(const Value: FLOAT);
begin
  if(Value>=0)and(Value<=100)then
  begin
    FX.fDepth:=Value;
    BASS_FXSetParameters(Handle,@FX);
  end;
end;

procedure TGPFX_Flanger.SetFeedback(const Value: FLOAT);
begin
  if(Value>=-99)and(Value<=99)then
  begin
    FX.fFeedback:=Value;
    BASS_FXSetParameters(Handle,@FX);
  end;
end;

procedure TGPFX_Flanger.SetFrequency(const Value: FLOAT);
begin
  if(Value>=0)and(Value<=10)then
  begin
    FX.fFrequency:=Value;
    BASS_FXSetParameters(Handle,@FX);
  end;
end;

procedure TGPFX_Flanger.SetPhase(const Value: DWORD);
begin
  if(Value<=4)then
  begin
    FX.lPhase:=Value;
    BASS_FXSetParameters(Handle,@FX);
  end;
end;

procedure TGPFX_Flanger.SetWaveform(const Value: DWORD);
begin
  if(Value<=1)then
  begin
    FX.lWaveform:=Value;
    BASS_FXSetParameters(Handle,@FX);
  end;
end;

procedure TGPFX_Flanger.SetWetDryMix(const Value: FLOAT);
begin
  if(Value>=0)and(Value<=100)then
  begin
    FX.fWetDryMix:=Value;
    BASS_FXSetParameters(Handle,@FX);
  end;
end;

{ TGPFX_Gargle }

constructor TGPFX_Gargle.Create(MusicHandle: Cardinal);
begin
  inherited Create;
  Music:=MusicHandle;
  ok:=player.AddFX(BASS_FX_DX8_GARGLE,Music,handle,@FX);
end;

destructor TGPFX_Gargle.Destroy;
begin
  BASS_ChannelRemoveFX(Music,Handle);
  inherited;
end;

function TGPFX_Gargle.RestoreDefaults: boolean;
begin
  FX:=DefaultStore.Gargle;
  result:=BASS_FXSetParameters(Handle,@FX);
end;

procedure TGPFX_Gargle.SetRateHZ(const Value: DWORD);
begin
  if(Value<=1000)then
  begin
    FX.dwRateHz:=Value;
    BASS_FXSetParameters(Handle,@FX);
  end;
end;

procedure TGPFX_Gargle.SetWaveShape(const Value: DWORD);
begin
  if(Value<=1)then
  begin
    FX.dwWaveShape:=Value;
    BASS_FXSetParameters(Handle,@FX);
  end;
end;

{ TGPFX_I3DL2Reverb }

constructor TGPFX_I3DL2Reverb.Create(MusicHandle: Cardinal);
begin
  Music:=MusicHandle;
  ok:=player.AddFX(BASS_FX_DX8_I3DL2REVERB,Music,Handle,@FX);
end;

destructor TGPFX_I3DL2Reverb.Destroy;
begin
  BASS_ChannelRemoveFX(Music,Handle);
  inherited;
end;

function TGPFX_I3DL2Reverb.RestoreDefaults: boolean;
begin
  FX:=DefaultStore.I3DL2Reverb;
  result:=BASS_FXSetParameters(Handle,@FX);
end;

procedure TGPFX_I3DL2Reverb.SetDecayHFRatio(const Value: FLOAT);
begin
  if(Value>=0.1)and(Value<=2)then
  begin
    FX.flDecayHFRatio:=Value;
    BASS_FXSetParameters(Handle,@FX);
  end;
end;

procedure TGPFX_I3DL2Reverb.SetDecayTime(const Value: FLOAT);
begin
  if(Value>=0.1)and(Value<=20)then
  begin
    FX.flDecayTime:=Value;
    BASS_FXSetParameters(Handle,@FX);
  end;
end;

procedure TGPFX_I3DL2Reverb.SetDensity(const Value: FLOAT);
begin
  if(Value>=0)and(Value<=100)then
  begin
    FX.flDensity:=Value;
    BASS_FXSetParameters(Handle,@FX);
  end;
end;

procedure TGPFX_I3DL2Reverb.SetDiffusion(const Value: FLOAT);
begin
  if(Value>=0)and(Value<=100)then
  begin
    FX.flDiffusion:=Value;
    BASS_FXSetParameters(Handle,@FX);
  end;
end;

procedure TGPFX_I3DL2Reverb.SetHFReference(const Value: FLOAT);
begin
  if(Value>=20)and(Value<=20000)then
  begin
    FX.flHFReference:=Value;
    BASS_FXSetParameters(Handle,@FX);
  end;
end;

procedure TGPFX_I3DL2Reverb.SetReflectionsDelay(const Value: FLOAT);
begin
  if(Value>=0)and(Value<=0.3)then
  begin
    FX.flReflectionsDelay:=Value;
    BASS_FXSetParameters(Handle,@FX);
  end;
end;

procedure TGPFX_I3DL2Reverb.SetReflections(const Value: Integer);
begin
  if(Value>=-10000)and(Value<=1000)then
  begin
    FX.lReflections:=Value;
    BASS_FXSetParameters(Handle,@FX);
  end;
end;

procedure TGPFX_I3DL2Reverb.SetReverb(const Value: Integer);
begin
  if(Value>=-10000)and(Value<=2000)then
  begin
    FX.lReverb:=Value;
    BASS_FXSetParameters(Handle,@FX);
  end;
end;

procedure TGPFX_I3DL2Reverb.SetReverbDelay(const Value: FLOAT);
begin
  if(Value>=0)and(Value<=0.1)then
  begin
    FX.flReverbDelay:=Value;
    BASS_FXSetParameters(Handle,@FX);
  end;
end;

procedure TGPFX_I3DL2Reverb.SetRoom(const Value: Integer);
begin
  if(Value>=-10000)and(Value<=0)then
  begin
    FX.lRoom:=Value;
    BASS_FXSetParameters(Handle,@FX);
  end;
end;

procedure TGPFX_I3DL2Reverb.SetRoomHF(const Value: Integer);
begin
  if(Value>=-10000)and(Value<=0)then
  begin
    FX.lRoomHF:=Value;
    BASS_FXSetParameters(Handle,@FX);
  end;
end;

procedure TGPFX_I3DL2Reverb.SetRoomRolloffFactor(const Value: FLOAT);
begin
  if(Value>=0)and(Value<=10)then
  begin
    FX.flRoomRolloffFactor:=Value;
    BASS_FXSetParameters(Handle,@FX);
  end;
end;

{ TGPFX_Reverb }

constructor TGPFX_Reverb.Create(MusicHandle: Cardinal);
begin
  inherited Create;
  Music:=MusicHandle;
  ok:=player.AddFX(BASS_FX_DX8_REVERB,Music,Handle,@FX);
end;

destructor TGPFX_Reverb.Destroy;
begin
  BASS_ChannelRemoveFX(Music,handle);
  inherited;
end;

function TGPFX_Reverb.RestoreDefaults: boolean;
begin
  FX:=DefaultStore.Reverb;
  result:=BASS_FXSetParameters(Handle,@FX);
end;

procedure TGPFX_Reverb.SetHighFreqRTRatio(const Value: FLOAT);
begin
  if(Value>=0.001)and(Value<=0.999)then
  begin
    FX.fHighFreqRTRatio:=Value;
    BASS_FXSetParameters(Handle,@FX);
  end;
end;

procedure TGPFX_Reverb.SetInGain(const Value: FLOAT);
begin
  if(Value>=-96)and(Value<=0)then
  begin
    FX.fInGain:=Value;
    BASS_FXSetParameters(Handle,@FX);
  end;
end;

procedure TGPFX_Reverb.SetReverbMix(const Value: FLOAT);
begin
  if(Value>=-96)and(Value<=0)then
  begin
    FX.fReverbMix:=Value;
    BASS_FXSetParameters(Handle,@FX);
  end;
end;

procedure TGPFX_Reverb.SetReverbTime(const Value: FLOAT);
begin
  if(Value>=0.001)and(Value<=3000)then
  begin
    FX.fReverbTime:=Value;
    BASS_FXSetParameters(Handle,@FX);
  end;
end;

{ TGPFX_Equalizer }

constructor TGPFX_Equalizer.Create(MusicHandle: Cardinal);
var
  a:integer;
const
  bands:array[0..9]of FLOAT=
    (80,170,310,600,1000,3000,6000,12000,14000,15000);
  widths:array[0..8]of FLOAT=
    (13.0495540950041,10.4008016296384,11.4323314226597,8.84358712999448,
     19.0195500086539,12,12,2.66870905603738,1.19442808261097);
begin
  inherited Create;
  Music:=MusicHandle;ok:=true;
  for a:=0 to 8 do
    if Player.AddFX(BASS_FX_DX8_PARAMEQ,Music,Handle[a],@fx[a]) then
    begin
      FX[a].fCenter:=(bands[a]+bands[a+1])/2;
      FX[a].fBandwidth:=widths[a];
      FX[a].fGain:=0;
      if not BASS_FXSetParameters(handle[a],@FX[a]) then
        ok:=false;
    end else ok:=false;
end;

destructor TGPFX_Equalizer.Destroy;
var
  a:integer;
begin
  for a:=0 to 8 do
    BASS_ChannelRemoveFX(Music,Handle[a]);
  inherited;
end;

function TGPFX_Equalizer.GetBand(Index: Integer): FLOAT;
begin
  if(Index>=0)and(Index<=8)then result:=FX[Index].fGain else result:=0;
end;

function TGPFX_Equalizer.RestoreDefaults: boolean;
var
  a:integer;
begin
  result:=true;
  FX:=DefaultStore.Equalizer;
  for a:=0 to 8 do
    if not BASS_FXSetParameters(Handle[a],@FX[a]) then result:=false;
end;

procedure TGPFX_Equalizer.SetBand(Index: Integer; const Value: FLOAT);
begin
  if(Index>=0)and(Index<=8)and(Value>=-15)and(Value<=15)then
  begin
    FX[Index].fGain:=Value;
    BASS_FXSetParameters(Handle[Index],@FX[Index]);
  end;
end;
{$ENDIF}

{ TGPMusic }

function TGPMusic.Close: boolean;
begin
  result:=false;
  if FMusicType<>mtNone then
  begin
    if FMusicState<>msIdle then result:=Stop else result:=true;
    if result then
    begin
      SaveState(FStoredConfig);{$IFDEF GP_EFFECTS}FEffects.Free;FEffects:=nil;{$ENDIF}
      case FMusicType of
        mtMusic:begin result:=BASS_MusicFree(FHandle);result:=result or BASS_SampleFree(FSample);end;
        mtStream:begin result:=BASS_StreamFree(FHandle);result:=result or BASS_SampleFree(FSample);end;
        {$IFDEF GP_PLUGINS}mtPlugin:begin result:=true;{$IFDEF GP_EFFECTS}FEffects.Free;{$ENDIF}end;{$ENDIF}
      end;
    end;
  end;
  if result then
  begin
    {$IFDEF GP_PLUGINS}Player.FPlugins.FCurPlugin:=nil;{$ENDIF}
    Player.FMusicSupport:='';
    FMusicType:=mtNone;
    FHandle:=0;
  end;
end;

constructor TGPMusic.Create;
begin
  inherited Create;
  FisSound:=true;FFileName:='';FMusicType:=mtNone;FMusicState:=msIdle;
  FnLoops:=0;FHandle:=0;FSample:=0;FPan:=0;FVolume:=100;
  {$IFDEF GP_EFFECTS}
  FEffects:=nil;FUseEAX:=false;FCanStore:=true;
  ZeroMemory(@FStoredConfig,sizeof(FStoredConfig));
  {$ENDIF}
  FStoredConfig.Volume:=FVolume;
  FStoredConfig.Pan:=FPan;
  FIndex:=Length(Player.FMusicsList);
  SetLength(Player.FMusicsList,FIndex+1);Player.FMusicCount:=FIndex+1;
  Player.FMusicsList[FIndex]:=Self;
  FOnPlayEnd:=nil;
end;

destructor TGPMusic.Destroy;
var
  a,n:integer;
begin
  Close;
  {$IFDEF GP_EFFECTS}if FEffects<>nil then FEffects.Free;{$ENDIF}
  n:=length(Player.FMusicsList);
  if FIndex<(n-1) then
    for a:=FIndex+1 to n-1 do
    begin
      Player.FMusicsList[a-1]:=Player.FMusicsList[a];
      Player.FMusicsList[a-1].FIndex:=a-1;
    end;
  SetLength(Player.FMusicsList,n-1);Player.FMusicCount:=n-1;
  inherited;
end;

function TGPMusic.DoPlay(Resume: boolean): boolean;
begin
  result:=false;
  {$IFDEF GP_PLAYLIST}if(not Resume)and(not FisSound)then Player.PlayList.FTempDisable:=true;{$ENDIF}
  if FMusicType=mtNone then exit;
  //if(FMusicState<>msIdle)and(not resume)then result:=Stop else result:=true;
  result:=true;
  if result then
    {$IFDEF GP_PLUGINS}
    case FMusicType of
      mtMusic,mtStream:
      begin
    {$ENDIF}
        result:=BASS_ChannelPlay(FHandle,not Resume)
    {$IFDEF GP_PLUGINS}
      end;
      mtPlugin:
        if resume then
          begin Player.FPlugins.FCurPlugin.UnPause;result:=true;end
        else
        begin
          {$IFDEF GP_EFFECTS}if FEffects<>nil then if Effects.FMusic=0 then{$ENDIF} SaveState(FStoredConfig);
          result:=(Player.FPlugins.FCurPlugin.Play(PAnsiChar(FFilename))=0);
          if result then
          begin
            FHandle:=HBASSStream;
            {$IFDEF GP_EFFECTS}
            FEffects:=TGPEffects.Create(HBASSStream);
            LoadState(FStoredConfig);
            {$ENDIF}
          end;
        end;
    end
    {$ENDIF}
  else exit;
  if result then
  begin
    if not resume then
    begin
      if Assigned(FOnPlayStart) then FOnPlayStart(Self);
        SendMessage(Player.FMainWindow,WM_GP_PLAYSTART,
        {$IFDEF GP_PLAYLIST}Player.PlayList.MusicIndex{$ELSE}-1{$ENDIF},FTag);
    end;
    FMusicState:=msPlaying;
  end;
  //TerminateThread(FHandle,0);
end;

function TGPMusic.GetFFTData(var data: TGPFFTData): boolean;
var
  x:array[0..255]of FLOAT;
  a,b,n:integer;
  tmp:float;
begin
  result:=false;
  if FMusicType<>mtNone then
    if FMusicState=msPlaying then
    begin
      n:=BASS_ChannelGetData(FHandle,@x,BASS_DATA_FFT512);
      if n>0 then
      begin
        for a:=0 to 31 do
        begin
          tmp:=0;
          for b:=0 to 7 do
            tmp:=tmp+abs(x[a*8+b]*256);
          data[a]:=trunc(tmp/8);
        end;
        result:=true;
      end;
    end;
end;

function TGPMusic.GetLevel: DWORD;
begin
  if(FMusicType<>mtNone){$IFDEF GP_PLUGINS}and(FMusicType<>mtPlugin){$ENDIF}and(FMusicState<>msIdle)then
    result:=BASS_ChannelGetLevel(FHandle)
  else
    result:=0;
end;

function TGPMusic.GetPosition: Integer;
begin
  result:=0;
  if FMusicType<>mtNone then
    if FMusicState<>msIdle then
    {$IFDEF GP_PLUGINS}
    case FMusicType of
      mtStream,mtMusic:
    {$ENDIF}result:=trunc(1000*BASS_ChannelBytes2Seconds(FHandle,BASS_ChannelGetPosition(FHandle,BASS_POS_BYTE)));
    {$IFDEF GP_PLUGINS}
      mtPlugin:result:=Player.FPlugins.FCurPlugin.GetOutputTime;
    end;
    {$ENDIF}
end;

procedure TGPMusic.LoadState(var store:TGPStore);
begin
  {$IFDEF GP_EFFECTS}
  if FEffects=nil then
  begin
    FEffects:=TGPEffects.Create(0);
    FEffects.FCreateAnyway:=true;
  end;
  FEffects.LoadConfig(Store);
  FUseEAX:=Store.UseEAX;
  {$ENDIF}
  Pan:=Store.Pan;
  Volume:=Store.Volume;
end;

procedure TGPMusic.SaveState(var store:TGPStore);
begin
  {$IFDEF GP_EFFECTS}
  if FEffects<>nil then
    FEffects.SaveConfig(Store)
  else
    FillChar(Store,sizeof(Store),0);
  Store.UseEAX:=FUseEAX;
  {$ENDIF}
  Store.Pan:=FPan;
  Store.Volume:=FVolume;
end;

function TGPMusic.Open(MusicFileName: string): boolean;
var
  f:cardinal;
  {$IFDEF GP_EFFECTS}e:boolean;{$ENDIF}
begin
  if FMusicType<>mtNone then
  begin
    result:=Close;
    if not result then exit;
  end;
  FFileName:=MusicFileName;FMusicType:=mtNone;FMusicState:=msIdle;
  FnLoops:=0;FHandle:=0;FSample:=0;FPan:=0;
  FChannelLength:=0;
  {$IFDEF GP_EFFECTS}
  e:=Player.EAX.Enabled and FUseEAX;
  if e then
    f:=player.FFlags or BASS_SAMPLE_3D or BASS_SAMPLE_MONO
  else
  {$ENDIF}
    f:=Player.FFlags;
  if FileExist(MusicFileName) then
  begin
    {$IFDEF GP_EFFECTS}
    if e then
    begin
      FSample:=BASS_SampleLoad(FALSE, PChar(FileName),0,0,10,F);
      if FSample<>0 then FHandle:=BASS_SampleGetChannel(FSample,false)
        else FHandle:=BASS_StreamCreateFile(false,PAnsiChar(FileName),0,0,F);
    end
    else
    {$ENDIF}
      FHandle:=BASS_StreamCreateFile(false,PAnsiChar(FileName),0,0,f);
    if FHandle=0 then
    begin
      FHandle:=BASS_StreamCreateFile(false,PAnsiChar(FileName),0,0,Player.FSafeFlags);
      if FHandle=0 then
      begin
        FHandle:=BASS_MusicLoad(false,PAnsiChar(FileName),0,0,f or BASS_MUSIC_RAMPS or BASS_MUSIC_FT2MOD or BASS_MUSIC_PRESCAN,0);
        if FHandle=0 then
        begin
          FHandle:=BASS_MusicLoad(false,PAnsiChar(FileName),0,0,Player.FSafeFlags or BASS_MUSIC_RAMPS or BASS_MUSIC_FT2MOD or BASS_MUSIC_PRESCAN,0);
          if FHandle=0 then
          begin
            {$IFDEF GP_PLUGINS}if Player.Plugins.ActivatePlugin(FileName) then
              FMusicType:=mtPlugin
            else{$ENDIF}
              FMusicType:=mtNone
          end else FMusicType:=mtMusic;
        end else FMusicType:=mtMusic;
      end else FMusicType:=mtStream;
    end else FMusicType:=mtStream;
  end
  else
    {$IFDEF GP_INTERNET}
    if Player.IsSupportedURL(MusicFileName)then
    begin
      FHandle:=BASS_StreamCreateURL(PAnsiChar(MusicFileName),0,BASS_STREAM_BLOCK,nil,nil);
      if FHandle=0 then
        FMusicType:=mtNone
      else
        FMusicType:=mtURL;
    end
    else
    {$ENDIF}
      FMusicType:=mtNone;
  result:=(FMusicType<>mtNone);
//////////
  if result then
  begin
    {$IFDEF GP_PLUGINS}
    if FMusicType<>mtPlugin then
    begin
    {$ENDIF}
      {$IFDEF GP_EFFECTS}
      if FEffects=nil then FEffects:=TGPEffects.Create(FHandle)
      else
      begin
        Feffects.SaveConfig(FStoredConfig);
        FreeAndNil(FEffects);
        FEffects:=TGPEffects.Create(Fhandle);
        FEffects.LoadConfig(FStoredConfig);
      end;
      {$ENDIF}
      Player.FMusicSupport:='Bass.dll';
      {$IFDEF GP_INTERNET}
      if FMusicType<>mtURL then
      begin
      {$ENDIF}
        UpdateInfo;
        if FEndOffset=0 then
          BASS_ChannelSetSync(FHandle,BASS_SYNC_END,f,@MusicSyncProc,Self)
        else
        begin
          f:=BASS_ChannelGetLength(FHandle,BASS_POS_BYTE);
          if FEndOffset>f then
            f:=0
          else
            f:=f-FEndOffset;
          BASS_ChannelSetSync(FHandle,BASS_SYNC_POS,f,@MusicSyncProc,Self);
        end;
      {$IFDEF GP_INTERNET}
      end
      else
      begin
        UpdateInfoURL;
        BASS_ChannelSetSync(FHandle,BASS_SYNC_META,0,@MusicSyncProcTags,Self);
      end;
      {$ENDIF}
    {$IFDEF GP_PLUGINS}
    end
    else
    begin
      {$IFDEF GP_EFFECTS}
      if FEffects=nil then
      begin
        FEffects:=TGPEffects.Create(0);
        FEffects.FCreateAnyway:=true;
      end;
      {$ENDIF}
      UpdateInfo;
    end;
    {$ENDIF}
    LoadState(FStoredConfig);
    Volume:=FVolume;
    Pan:=FPan;
  end;
end;

function TGPMusic.Pause: boolean;
var
  tmp:integer;
begin
  result:=false;
  if FMusicType<>mtNone then
    if FMusicState=msPlaying then
    {$IFDEF GP_PLUGINS}
    case FMusicType of
      mtMusic,mtStream:{$ENDIF}
      begin
        tmp:=Volume;
        Volume:=0;
        result:=BASS_ChannelPause(FHandle);
        Volume:=tmp;
      end;
      {$IFDEF GP_PLUGINS}mtPlugin:begin Player.FPlugins.FCurPlugin.Pause;result:=true;end;
    end;
    {$ENDIF}
  if result then FMusicState:=msPaused;
end;

function TGPMusic.Play(PlayCount:word=1): boolean;
begin
  //if(not FisSound)and(Player.PlayList.Mode<>pmDisabled) then
  //  Player.PlayList.PlayOffset(0)
  //else
  //begin
    FnLoops:=PlayCount;
    result:=DoPlay(false);
  //end;
end;

function TGPMusic.Reload(KeepPosition:boolean): boolean;
var
  pos:integer;
  k:boolean;
  st:TGPMusicState;
begin
  st:=FMusicState;k:=(KeepPosition)and(st<>msIdle);
  if K then
  begin
    pos:=Position;
    result:=Close;
    if result then result:=Open(FFileName);
    Play;
    Position:=pos;
    if st=msPlaying then DoPlay(true) else if st=msPaused then Pause;
  end
  else
  begin
    result:=Close;
    if result then result:=Open(FFileName);
  end;
end;

procedure TGPMusic.SetPan(const Value: integer);
var
  ok:boolean;
begin
  ok:=false;
  if(Value>=-100)and(Value<=100)then
    {$IFDEF GP_PLUGINS}if FMusicType=mtPlugin then begin ok:=true;Player.FPlugins.FCurPlugin.SetPan(trunc(Value/100*127));end
    else
  {$ENDIF}
      if FHandle<>0 then
        ok:=BASS_ChannelSetAttribute(FHandle,BASS_ATTRIB_PAN,Value/100)
      else
        ok:=true;
  if ok then
  begin
    FStoredConfig.Pan:=Value;
    FPan := Value;
  end;
end;

procedure TGPMusic.SetPosition(const Value: Integer);
begin
  if FMusicType<>mtNone then
    if FMusicState<>msIdle then
    {$IFDEF GP_PLUGINS}
      case FMusicType of
        mtStream,mtMusic:{$ENDIF}BASS_ChannelSetPosition(FHandle,BASS_ChannelSeconds2Bytes(FHandle,Value/1000),BASS_POS_BYTE);
        {$IFDEF GP_PLUGINS}mtPlugin:Player.FPlugins.FCurPlugin.SetOutputTime(Value);
      end;
    {$ENDIF}
end;

{$IFDEF GP_EFFECTS}
procedure TGPMusic.SetUseEAX(const Value: boolean);
begin
  FUseEAX := Value;
  Reload(true);
end;
{$ENDIF}

procedure TGPMusic.SetVolume(const Value: integer);
var
  ok:boolean;
begin
  ok:=false;
  if(Value>=0)and(Value<=100)then
  {$IFDEF GP_PLUGINS}
    if FMusicType=mtPlugin then begin ok:=true;Player.FPlugins.FCurPlugin.SetVolume(trunc(Value/100*255));end
    else
  {$ENDIF}
      if FHandle<>0 then
        ok:=BASS_ChannelSetAttribute(FHandle,BASS_ATTRIB_VOL,Value/100)
      else
        ok:=true;
  if ok then
  begin
    FStoredConfig.Volume:=Value;
    FVolume := Value;
  end;
end;

function TGPMusic.Stop: boolean;
begin
  result:=false;
  if FMusicType<>mtNone then
    if FMusicState<>msIdle then
    begin
      {$IFDEF GP_PLUGINS}
      case FMusicType of
        mtMusic,mtStream:{$ENDIF}result:=BASS_ChannelStop(FHandle);
      {$IFDEF GP_PLUGINS}
        mtPlugin:
        begin
          {$IFDEF GP_EFFECTS}SaveState(FStoredConfig);{$ENDIF}
          Player.FPlugins.FCurPlugin.Stop;
          SendMessage(Player.FMainWindow,WM_StreamClose,0,0);result:=true;
        end;
      end;
      {$ENDIF}
    end;
  if result then
  begin
    FMusicState:=msIdle;
  end;
end;

function TGPMusic.TogglePause: boolean;
begin
  if FMusicType<>mtNone then
    if FMusicState=msPaused then
      result:=UnPause
    else
      result:=Pause
  else result:=false;
end;

function TGPMusic.UnPause: boolean;
begin
  result:=false;
  if FMusicType<>mtNone then
    if FMusicState=msPaused then
      result:=DoPlay(true);
end;

function TGPMusic.UpdateInfo: boolean;
type
  TTagInfo=record
    NAME:array[0..3]of char;
    LEN:Cardinal;
    FLAGS:array[0..1]of byte;
    ENCODING:byte;
  end;
  PTagInfo=^TTagInfo;

var
  _t:array[0..MAX_PATH]of Char;
  {$IFDEF GP_PLUGINS}_l:integer;{$ENDIF}
  ext:string;
  Tags:PAnsichar;
  a,nn:integer;
  s1,s2:string;
  fullname:string;
  taginfo:PTagInfo;
  _tpe2:string;

procedure GetChunkedData(AuthorChunk:string;TitleChunk:string);
var
  a:integer;
  chunk,data:string;
begin
  s1:='';s2:='';if tags=nil then exit;
  a:=0;if tags[0]=#0 then exit;
  while tags[a]<>#0 do
  begin
    chunk:='';
    while tags[a]<>'=' do
    begin
      chunk:=chunk+tags[a];
      inc(a);
    end;
    inc(a);data:='';
    while tags[a]<>#0 do
    begin
      data:=data+tags[a];
      inc(a);
    end;
    inc(a);
    if UpperCase(chunk)=UpperCase(TitleChunk) then s1:=data else
      if UpperCase(chunk)=UpperCase(AuthorChunk) then s2:=data;
  end;
end;

function Swap32(const x: Integer): Integer;
var
  y: array [1..4] of byte absolute x;
begin
  Result:=(y[1] shl 24)+(y[2] shl 16)+(y[3] shl 8)+y[4];
end;

function IsNotLetterOrDigit(c:char):boolean;inline;
begin
  result:=((c<'A')or(c>'Z'))and((c<'0')or(c>'9'));
end;

function getstr(data:PAnsiChar;_start,_end:integer;encoding:integer):string;
var
  a:integer;
  w:WideString;
  buf:array[0..255]of WideChar;
begin
  if encoding=1 then // UTF-16
    if(data[_start]=#255)and(data[_start+1]=#254)then // LE
    begin
      a:=_start+2;w:='';
      while(a<=_end)do
      begin
        w:=w+WideChar((ord(data[a]))+(ord(data[a+1]) shl 8));
        a:=a+2;
      end;
      result:=string(w);
    end
    else if(data[_start+1]=#255)and(data[_start]=#254)then // BE
    begin
      a:=_start+2;w:='';
      while(a<=_end)do
      begin
        w:=w+WideChar((ord(data[a+1]))+(ord(data[a]) shl 8));
        a:=a+2;
      end;
      result:=string(w);
    end
    else // Unknown UTF-16 format, using ISO
    begin
      result:='';
      for a:=_start to _end do
        result:=result+data[a];
    end
  else
  if encoding<>3 then // ISO or unknown
  begin
    result:='';
    for a:=_start to _end do
      result:=result+data[a];
  end
  else // UTF-8
  begin
    a:=MultiByteToWideChar(CP_UTF8,0,(data+_start),_end-_start,@buf,sizeof(buf));
    buf[a]:=#0;
    result:=string(Widestring(buf));
  end;
end;

begin
  zeromemory(@_t,sizeof(_t));
  s1:='';s2:='';
  fullname:=GetFullPath(FFileName);
  if FMusicType<>mtNone then
  begin
    {$IFDEF GP_PLUGINS}
    if FMusicType=mtPlugin then
    begin
      Player.FPlugins.FCurPlugin.GetFileInfo(PAnsiChar(fullname),_t,_l);
      s1:=_t;FChannelLength:=_l;
    end
    else
    begin
    {$ENDIF}
      FChannelLength:=trunc(1000*Bass_channelbytes2seconds(FHandle,BASS_ChannelGetLength(FHandle,BASS_POS_BYTE)));
      result:=(FChannelLength<>DWORD(-1000));
      if result then
      begin
        ext:=UpperCase(ExtractFileExt(FFileName));
        if(ext='.MP3')or(ext='.MP2')or(ext='.MP1')then
        begin
          tags:=BASS_ChannelGetTags(FHandle,BASS_TAG_ID3V2);
          if tags<>nil then
          begin
            a:=10;_tpe2:='';
            while true do
            begin
              taginfo:=PTagInfo(@tags[a]);
              if IsNotLetterOrDigit(taginfo.NAME[0])or
                 IsNotLetterOrDigit(taginfo.NAME[1])or
                 IsNotLetterOrDigit(taginfo.NAME[2])or
                 IsNotLetterOrDigit(taginfo.NAME[3])then break;
              a:=a+sizeof(TTagInfo)-1;
              nn:=a+Swap32(taginfo.LEN)-2;
              if taginfo.NAME='TIT2' then
                s1:=getstr(tags,a,nn,taginfo.ENCODING)
              else
              if taginfo.NAME='TPE1' then
                s2:=getstr(tags,a,nn,taginfo.ENCODING)
              else
              if taginfo.NAME='TPE2' then
                _tpe2:=getstr(tags,a,nn,taginfo.ENCODING);
              if(s1<>'')and(s2<>'')then break;
              a:=nn+1;
            end;
            if (s2='')and(_tpe2<>'')then s2:=_tpe2;
          end
          else
          begin
            tags:=BASS_ChannelGetTags(FHandle,BASS_TAG_ID3);
            if tags<>nil then
            begin
              for a:=0 to 29 do _t[a]:=tags[a+3];_t[30]:=#0;s1:=_t;
              for a:=0 to 29 do _t[a]:=tags[a+33];_t[30]:=#0;s2:=_t;
            end;
          end;
        end
        else
        if(ext='.MO3')or(ext='.IT')or(ext='.XM')or(ext='.S3M')
          or(ext='.MTM')or(ext='.MOD')or(ext='.UMX') then
        begin
          tags:=BASS_ChannelGetTags(FHandle,BASS_TAG_MUSIC_NAME);
          if tags<>nil then
            s1:=string(tags);
        end
        else
        if(ext='.OGG')then
        begin
          tags:=BASS_ChannelGetTags(FHandle,BASS_TAG_OGG);
          GetChunkedData('ARTIST','TITLE');
        end
        else
        if(ext='.WAV')then
        begin
          tags:=BASS_ChannelGetTags(FHandle,BASS_TAG_RIFF_INFO);
          GetChunkedData('IART','INAM');
        end;
      end;
    end;
    a:=length(s1);
    if a>0 then
    begin
      while(ord(s1[a])<=32)and(a>0) do dec(a);SetLength(s1,a);
    end;
    a:=length(s2);
    if a>0 then
    begin
      while(ord(s2[a])<=32)and(a>0) do dec(a);SetLength(s2,a);
    end;
    if s1='' then FTitle:=ExtractFileNameOnly(FFileName)
    else
      if s2='' then FTitle:=s1
        else FTitle:=s2+' - '+s1;
  {$IFDEF GP_PLUGINS}end;{$ENDIF}
  result:=true;
end;

{$IFDEF GP_INTERNET}
function TGPMusic.UpdateInfoURL: boolean;
var
  tags:pointer;
  p:integer;
begin
  FTitle:='';
  result:=false;
  tags:=BASS_ChannelGetTags(FHandle,BASS_TAG_META);
  if tags<>nil then
  begin
    if Length(PAnsiChar(tags))>15 then
      if copy(UpperCase(PAnsiChar(tags)),1,13)='STREAMTITLE=''' then
      begin
        p:=pos(''';',PAnsiChar(tags));
        if p>15 then
          FTitle:=copy(PAnsiChar(tags),14,p-14);
      end;
  end
  else
  begin
    tags:=BASS_ChannelGetTags(FHandle,BASS_TAG_ICY);
    if tags<>nil then
    begin
      //
    end
  end;
end;
{$ENDIF}

{$IFDEF GP_PLAYLIST}
{ TGPPlayList }

function TGPPlayList.AddFile(FileName: string): boolean;
var
  n:integer;
  s:string;
  FName: PChar;
  Buffer: array[0..MAX_PATH - 1] of Char;
begin
  if FileName='' then begin result:=false;exit;;end;
  if InfoOnLoad then
  begin
    result:=Player.Music.Open(FileName);
    if result then s:=Player.Music.Title else s:='';
    if not result then exit;
  end else begin result:=true;s:=ExtractFileNameOnly(FileName);end;
  if result then
  begin
    n:=Length(FFiles);
    SetLength(FFiles,n+1);SetLength(FTitles,n+1);
    {$IFDEF GP_INTERNET}
    if Player.IsSupportedURL(FileName) then
      FFiles[n]:=FileName
    else
    {$ENDIF}
      SetString(FFiles[n],Buffer,GetFullPathName(PChar(FileName),SizeOf(Buffer),Buffer,FName));
    FTitles[n]:=s;
    FCount:=n+1;
  end;
end;

function TGPPlayList.AddFromFolder(Dir:string;SubDirs:boolean;DoClear:boolean=false):boolean;
var
  F:_WIN32_FIND_DATA;
  h:THandle;
  n,nn:integer;
  s:string;
begin
  n:=length(dir);if dir[n]='\' then s:=dir else s:=dir+'\';
  h:=FindFirstFile(PAnsiChar(s+'*'),f);nn:=0;result:=false;
  if h<>INVALID_HANDLE_VALUE then
  begin
    if DoClear then Clear;
    repeat
      if(string(f.cFileName)<>'.')and(string(f.cFileName)<>'..')then
        if((f.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY)<>0)and SubDirs then
          begin if AddFromFolder(s+f.cFileName,true,false) then result:=true;end
        else
          if AddFile(s+f.cFileName) then begin inc(nn);end;
    until not FindNextFile(h,f);
  end;
  FindClose(h);result:=result or (nn>0);
end;

procedure TGPPlayList.Clear;
begin
  Player.FMusic.Close;
  SetLength(FFiles,0);SetLength(FTitles,0);
  FMusicIndex:=-1;FCount:=0;
end;

constructor TGPPlayList.Create;
begin
  inherited;
  Clear;FMode:=pmDisabled;FInfoOnLoad:=true;
  FTempDisable:=true;
end;

function TGPPlayList.Delete(Index: integer): integer;
var
  a:integer;
begin
  result:=length(FFiles);
  if(Index<0)or(Index>=Result)then exit;
  if Index<(result-1) then
    for a:=Index+1 to result-1 do
    begin
      FFiles[a-1]:=FFiles[a];
      FTitles[a-1]:=FTitles[a];
    end;
  if(FMusicIndex>=Index)or(FMusicIndex=(result-1)) then dec(FMusicIndex);
  SetLength(FFiles,result-1);SetLength(FTitles,result-1);dec(Result);
  FCount:=result;
end;

destructor TGPPlayList.Destroy;
begin
  SetLength(FFiles,0);SetLength(FTitles,0);
  inherited;
end;

procedure TGPPlayList.Exchange(Index1, Index2: integer);
var
  n:integer;
  s:string;
begin
  n:=Length(FFiles);
  if(Index1<0)or(Index2<0)or(Index1>=n)or(Index2>=n)or(Index1=Index2)then exit;
  s:=FFiles[Index1];FFiles[Index1]:=FFiles[Index2];FFiles[Index2]:=s;
  s:=FTitles[Index1];FTitles[Index1]:=FTitles[Index2];FTitles[Index2]:=s;
  if FMusicIndex=Index1 then FMusicIndex:=Index2 else
  if FMusicIndex=Index2 then FMusicIndex:=Index1;
end;

procedure TGPPlayList.SetMusicIndex(Value:Integer);
begin
  Player.FMusic.Stop;
  FMusicIndex:=Value;
end;

function TGPPlayList.GetFiles(Index: integer):string;
begin
  result:=FFiles[Index];
end;

function TGPPlayList.GetTitles(Index: integer):string;
begin
  result:=FTitles[Index];
end;

function TGPPlayList.SaveToFile(FileName:string):boolean;
var
  f:textfile;
  a:integer;
begin
  result:=false;
  try
    AssignFile(f,FileName);
    Rewrite(f);
  except
    CloseFile(f);
    exit;
  end;
  try
    if FCount>0 then
      for a:=0 to FCount-1 do
        Writeln(f,Files[a]);
  except
    CloseFile(f);
    exit;
  end;
  try
    CloseFile(f);
  except
    exit;
  end;
  result:=true;
end;

function TGPPlayList.LoadFromFile(FileName:string;DoClear:boolean=true): boolean;
var
  f:textfile;
  n:integer;
  s,dir:string;

begin
  result:=false;
  if not FileExist(FileName) then exit;
  n:=0;
  try
    AssignFile(f,FileName);
    Reset(f);
  except
    CloseFile(f);
    exit;
  end;
  if DoClear then Clear;
  dir:=ExtractFileDir(FileName);
  if dir='' then dir:=GetProgDir;
  dir:=dir+'\';
  while not eof(f) do
  begin
    try
      Readln(f,s);
    except
      break
    end;
    if (length(s)>1) then
      if(s[2]<>':'){$IFDEF GP_INTERNET}and(Player.IsSupportedURL(s)=false){$ENDIF} then s:=dir+s;
    if AddFile(s) then inc(n);
  end;
  try
    CloseFile(f);
  except;
  end;
  result:=(n>0);
end;

function TGPPlayList.PlayCurrent: boolean;
begin
  result:=PlayOffset(0);
end;

function TGPPlayList.PlayMusic(FileIndex: integer): boolean;
begin
  FMusicIndex:=FileIndex;
  result:=PlayOffset(0);
end;

function TGPPlayList.PlayNext: boolean;
begin
  result:=PlayOffset(1);
end;

function TGPPlayList.PlayOffset(Offset: integer): boolean;
var
  Start,n,ofs:integer;
begin
  Start:=-1;result:=false;
  n:=length(FFiles);if(n=0) then exit;
  case FMode of
    pmDisabled:exit;
    pmSequence,pmLoopAll:ofs:=Sgn(Offset);
    pmLoopOne:ofs:=0;
    pmShuffle:ofs:=random(n)
  else
    ofs:=Sgn(Offset);
  end;
  repeat
    FMusicIndex:=FMusicIndex+Ofs;
    repeat
      if FMusicIndex<0 then FMusicIndex:=n+FMusicIndex else
      if FMusicIndex>=n then FMusicIndex:=FMusicIndex-n else break;
      if (FMode=pmSequence)or(FMode=pmLoopOne)then exit; 
    until false;
    if FMusicIndex=Start then exit;
    if Player.Music.Open(FFiles[FMusicIndex]) then
    begin
      FTitles[FMusicIndex]:=Player.Music.Title;
      result:=Player.Music.Play(1);
      FTempDisable:=false;
    end;
    if not result then
    begin
      Player.Music.Close;
      if Start=-1 then Start:=FMusicIndex;
      ofs:=sgn(offset);if ofs=0 then ofs:=1;
    end;
  until result;
end;

function TGPPlayList.PlayPrev: boolean;
begin
  result:=PlayOffset(-1);
end;

procedure TGPPlayList.Sort(SortMethod: TGPSortMethod;Backwards:boolean);
var
  a,b,n:integer;

  function Compare(Index1,Index2:integer):integer;
  begin
    case SortMethod of
      smFileName:
        result:=AnsiStrIComp(ExtractFileName(FFiles[Index1]),ExtractFileName(FFiles[Index2]));
      smTitle:
        result:=-AnsiStrIComp(FTitles[Index1],FTitles[Index2]);
      smPath:
        result:=AnsiStrIComp(FFiles[Index1],FFiles[Index2]);
      else result:=0;
    end;
    if Backwards then result:=-result;
  end;

begin
  n:=Length(FFiles);if n<2 then exit;
  if SortMethod<>smShuffle then
    for a:=0 to n-2 do
      for b:=a+1 to n-1 do
        begin if Compare(a,b)>0 then Exchange(a,b) end
  else
    for a:=0 to n-1 do
      Exchange(a,random(a));
end;
{$ENDIF}

{$IFDEF GP_PLUGINS}
{ TGPPlugins }

function TGPPlugins.ActivatePlugin(FileName: string): boolean;
var
  ext,pext:string;
  a,p,n:integer;
begin
  ext:=UpperCase(ExtractFileExt(FileName));n:=length(ext)-1;ext:=copy(ext,2,n);
  if Player.Plugins.FPluginsOK then
    for a:=0 to MaxPluginNum-1 do
    begin
      FCurPlugin:=pluginctrl.plugin[a];
      if FCurPlugin<>nil then
      begin
        pext:=';'+UpperCase(PluginInfo[a].FileExtensions)+';';
        p:=pos(';'+ext+';',pext);
        if p>0 then
        begin
          SelectInputPlugin(a);result:=true;
          Player.FMusicSupport:=ExtractFileName(PluginInfo[a].Name);
          exit;
        end;
      end;
    end;
  result:=false;
end;

constructor TGPPlugins.Create;
begin
  SetLength(FPlugList,0);SetLength(FHandList,0);
  nPlugins:=0;FPluginsOK:=false;
  FPluginsOK:=InitPluginCtrl;
end;

destructor TGPPlugins.Destroy;
begin
  if FPluginsOK then
  begin
    UnLoadPlugins;
    QuitPluginCtrl;
  end;
end;

function TGPPlugins.GetPlugin(Index: Integer): TPlugin;
begin
  result:=pluginctrl.Plugin[Index];
end;

function TGPPlugins.GetPluginInfo(Index: Integer): TPluginInfo;
begin
  result:=pluginctrl.PluginInfo[Index];
end;

function TGPPlugins.GetWinampPluginsDir: string;
var
  n:integer;
  s:PAnsiChar;
begin
  if FWinampDir<>'' then begin result:=FWinampDir;exit;end;
  n:=MAX_PATH;
  try
    s:=nil;
    if RegQueryValue(HKEY_CURRENT_USER,PAnsiChar('Software\Winamp'),s,n)=ERROR_SUCCESS then
    begin
      result:=string(s)+'\Plugins';
      FWinampDir:=result;
    end
      else result:='';
  except
    result:='';
  end;
end;

function TGPPlugins.LoadPlugin(FileName: string): boolean;
var
  a:integer;
begin
  if FPluginsOK<>true then begin result:=false;exit;end;
  if nPlugins>0 then
    for a:=0 to nPlugins-1 do
      if AnsiStrIComp(ExtractFileName(FileName),ExtractFileName(pluginctrl.PluginInfo[a].Name))=0 then
        begin result:=false;exit;end;
  try
    if LoadWinampPlugin(FileName)<>0 then begin result:=false;exit;end;
  except
    UnloadWinampPlugin(FileName);result:=false;exit;
  end;
  for a:=0 to pluginctrl.MaxPluginNum-1 do
  begin
    if(a<>nPlugins)and(pluginctrl.Plugin[a]=pluginctrl.Plugin[nPlugins])then
    begin
      pluginctrl.Plugin[nPlugins]:=nil;
      ZeroMemory(
        @pluginctrl.PluginInfo[nPlugins],
        sizeof(pluginctrl.PluginInfo[nPlugins]));
      result:=false;exit;
    end;
  end;
  result:=true;inc(nPlugins);Player.UpdateFilesFilter;
end;

function TGPPlugins.LoadPlugins(DirName: string): boolean;
var
  h:Cardinal;
  n:integer;
  s:string;
  f:_WIN32_FIND_DATA;
begin
  n:=length(DirName);if Dirname[n]='\' then s:=DirName else s:=DirName+'\';
  h:=FindFirstFile(PAnsiChar(s+'in_*.dll'),f);result:=false;
  if h<>INVALID_HANDLE_VALUE then
  repeat
    if LoadPlugin(s+f.cFileName) then result:=true;
  until not FindNextFile(h,f);
  FindClose(h);
end;

function TGPPlugins.LoadWinampPlugins: boolean;
var
  s:string;
begin
  s:=GetWinampPluginsDir;
  if s<>'' then result:=LoadPlugins(s+'\') else result:=false;
end;

procedure TGPPlugins.ShowAboutWindow(PluginNum: integer);
begin
  if(PluginNum>=0)and(PluginNum<nPlugins)then
    try ShowAboutWindow(PluginNum);except;end;
end;

procedure TGPPlugins.ShowConfigWindow(PluginNum: integer);
begin
  if(PluginNum>=0)and(PluginNum<nPlugins)then
    try ShowConfigWindow(PluginNum);except;end;
end;

procedure TGPPlugins.UnLoadPlugin(PluginNum: integer);
var
  a:integer;
begin
  if(PluginNum>=0)and(PluginNum<nPlugins)then
    try UnloadWinampPlugin(pluginctrl.PluginInfo[PluginNum].Name);except;exit;end;
  if PluginNum<(nPlugins-1) then
  begin
    for a:=PluginNum to nPlugins-2 do
    begin
      pluginctrl.Plugin[a]:=pluginctrl.Plugin[a+1];
      pluginctrl.PluginInfo[a]:=pluginctrl.PluginInfo[a];
    end;
  end;
  pluginctrl.Plugin[nPlugins-1]:=nil;
  ZeroMemory(
    @pluginctrl.PluginInfo[nPlugins-1],
    sizeof(pluginctrl.PluginInfo[nPlugins-1]));
  dec(nPlugins);
end;

procedure TGPPlugins.UnLoadPlugins;
var
  a:integer;
begin
  if nPlugins>=0 then
    for a:=0 to nPlugins-1 do
      try UnLoadPlugin(0);except;end;
  nPlugins:=0;
end;
{$ENDIF}

initialization

finalization
  if(Player<>nil)then
    if Player.FVerificationString=VerificationString then
      Player.Free;

end.
