Winamp Zone - Helper classes [delphi]

unit Winamp;

(*
** Winamp 1.x/2.x/5.x helper classes
** Version 1.0
** Slava Antonov (c) 2004
** http://slava.users.otts.ru
*)

interface

uses
{$ifndef NO_EXCEPTIONS}
  SysUtils,
{$endif}
  Windows, Messages, Player, WinampAPI, Utils;

const
  INDEX_CURRENT_TRACK = -1;

type
{ Forward declarations }
  TWinampVolume = class;
  TWinampPlaylist = class;
  TWinampTrack = class;
  TWinampVersion = class;

{ Winamp specefic events }
  TWinampBeforeCommandEvent = procedure(CommandID: Cardinal; var DontPass: Boolean) of object;
    { This event occurs before command passed to the
      winamp window procedure.
      If you set DontPass to True, command will not passed to the
      winamp window procedure, so you can use this event to
      override winamp commands like Hotamp override Jump To File
      command. }
  TWinampAfterCommandEvent = procedure(CommandID: Cardinal) of object;
    { This event occurs when command was passed to default
      winamp window procedure.
      So, if you set DontPass to True this event won't occur. }

  TWinamp = class(TPlayer)
  private
    FOldWndProc: Pointer;
      { Pointer to the original winamp window procedure }

    FOnBeforeCommand: TWinampBeforeCommandEvent;
    FOnAfterCommand: TWinampAfterCommandEvent;
  protected
    FhwndWinamp: THandle;

    procedure CallOldWndProc(var Message: TMessage);
    procedure WndProc(var Message: TMessage); virtual;
      { Used for winamp window subclassing }

    function GetMode: TPlayMode; override;
    procedure SetMode(const Value: TPlayMode); override;
    function GetState: TPlayState; override;
  public
    constructor Create(hwndWinamp: THandle);
    destructor Destroy; override;

    function SendMsg(Msg: Cardinal; wParam, lParam: Integer): Integer;
      { Send message to the main winamp window }

    procedure Prev; override;
    procedure Play; overload; override;
    procedure Play(const Path: String); overload; override;
    procedure Pause; override;
    procedure Stop; override;
    procedure Next; override;
    procedure Fastforward; override;
    procedure Rewind; override;

  { Winamp events }
    property OnBeforeCommand: TWinampBeforeCommandEvent read FOnBeforeCommand write FOnBeforeCommand;
    property OnAfterCommand: TWinampAfterCommandEvent read FOnAfterCommand write FOnAfterCommand;
  end;

  TWinampVolume = class(TVolume)
  private
    FWinamp: TWinamp;
  protected
    function GetValue: Byte; override;
    procedure SetValue(AValue: Byte); override;
  public
    constructor Create(AWinamp: TWinamp);
    procedure Increase; override;
    procedure Decrease; override;
  end;

  TWinampTrack = class(TTrack)
  private
    FWinamp: TWinamp;
    FIndex: Integer;
  protected
    function GetFileURL: String; override;
    function GetLength: Integer; override;
    function GetPosition: Integer; override;
    procedure SetPosition(Value: Integer); override;
    function GetTitle: String; override;
  public
    constructor Create(AWinamp: TWinamp);
    property Index: Integer read FIndex write FIndex;
  end;

{$ifndef NO_EXCEPTIONS}
  EWinampPlaylist = class(EPlaylist);
{$endif}
  TWinampPlaylist = class(TPlaylist)
  private
    FWinamp: TWinamp;
    FPlaylistFile: String;
    FTrack: TWinampTrack;
  protected
    function GetLength: Integer; override;
    function GetItem(Index: Integer): TTrack; override;
    function GetPlaylistFile: String; override;
    function GetPosition: Integer; override;
    procedure SetPosition(Value: Integer); override;
  public
    constructor Create(AWinamp: TWinamp);
    destructor Destroy; override;
    procedure Add(const Path: String); override;
    procedure Clear; override;
    procedure Load(const Path: String); override;
    procedure RemoveDeadFiles; override;
    procedure Save; overload; override;
    procedure Save(const PlaylistFile: String); overload; override;
    procedure Sort(SortMode: TSortMode); override;
  end;

  TWinampVersion = class(TPlayerVersion)
  public
    constructor Create(AWinamp: TWinamp);
  end;


  procedure CheckWinamp(hwndWinamp: THandle);

implementation

resourcestring
  SNeedWinampPlugin = 'Winamp helper classes can work only under winamp plugin';

{ TWinampVolume }

constructor TWinampVolume.Create(AWinamp: TWinamp);
begin
  FWinamp:= AWinamp;
end;

function TWinampVolume.GetValue: Byte;
begin
  Result:= FWinamp.SendMsg(WM_WA_IPC, -666, IPC_SETVOLUME);
end;

procedure TWinampVolume.SetValue(AValue: Byte);
begin
  FWinamp.SendMsg(WM_WA_IPC, AValue, IPC_SETVOLUME);
end;

procedure TWinampVolume.Decrease;
begin
  FWinamp.SendMsg(WM_COMMAND, WINAMP_VOLUMEDOWN, 0);
end;

procedure TWinampVolume.Increase;
begin
  FWinamp.SendMsg(WM_COMMAND, WINAMP_VOLUMEUP, 0);
end;

{ TWinampTrack }

constructor TWinampTrack.Create(AWinamp: TWinamp);
begin
  FWinamp:= AWinamp;
  FIndex:= INDEX_CURRENT_TRACK;
end;

function TWinampTrack.GetFileURL: String;
var
  Pos: Integer;
  pszFileURL: PChar;
begin
{ TODO : При удалении текущей композиции неправильно
  отображается информация о композиции }
  if FIndex = INDEX_CURRENT_TRACK then
    Pos:= FWinamp.SendMsg(WM_WA_IPC, 0, IPC_GETLISTPOS)
  else
    Pos:= FIndex;

  pszFileURL:= PChar(FWinamp.SendMsg(WM_WA_IPC,
                         Pos,
                         IPC_GETPLAYLISTFILE));
  if pszFileURL <> nil then
    Result:= pszFileURL
  else
    Result:= '';
end;

function TWinampTrack.GetLength: Integer;
begin
{ TODO : Сделать получение длительности композиции }
  Result:= FWinamp.SendMsg(WM_WA_IPC,
               1,
               IPC_GETOUTPUTTIME);
end;

function TWinampTrack.GetPosition: Integer;
begin
  if FIndex = INDEX_CURRENT_TRACK then
    Result:= FWinamp.SendMsg(WM_WA_IPC,
                 0,
                 IPC_GETOUTPUTTIME)
  else
{ TODO : Получение длительности произвольной композиции }
    Result:= 0;
end;

procedure TWinampTrack.SetPosition(Value: Integer);
begin
  if FIndex = INDEX_CURRENT_TRACK then
    FWinamp.SendMsg(WM_WA_IPC, Value, IPC_JumpToTime);
{ TODO : Позиционирование для произвольной композиции,
  а не только для текущей. }
end;

function TWinampTrack.GetTitle: String;
var
  Pos: Integer;
  pszTitle: PChar;
begin
{ TODO : При удалении текущей композиции неправильно
  отображается информация о композиции }
  if FIndex = INDEX_CURRENT_TRACK then
    Pos:= FWinamp.SendMsg(WM_WA_IPC, 0, IPC_GETLISTPOS)
  else
    Pos:= FIndex;

  pszTitle:= PChar(FWinamp.SendMsg(WM_WA_IPC,
                       Pos,
                       IPC_GETPLAYLISTTITLE));
  if pszTitle <> nil then
    Result:= pszTitle
  else
    Result:= '';
end;

{ TWinampPlaylist }

constructor TWinampPlaylist.Create(AWinamp: TWinamp);
var
  I, J: Integer;
  Buf: array[0..MAX_PATH] of Char;
  S: String;
begin
  FWinamp:= AWinamp;

{ Get path for the default playlist file }
  SetString(S,
      Buf,
      GetModuleFileName(0, Buf, MAX_PATH));
  J:= 0;
  for I:= System.Length(S) downto 1 do
    if S[I] = '\' then
    begin
      J:= I;
      Break;
    end;
  FPlaylistFile:= Copy(S, 1, J) + WINAMP_PLAYLIST_FILE;

  FTrack:= TWinampTrack.Create(FWinamp);
end;

destructor TWinampPlaylist.Destroy;
begin
  FTrack.Free;
end;

function TWinampPlaylist.GetLength: Integer;
begin
  Result:= FWinamp.SendMsg(WM_WA_IPC, 0, IPC_GETLISTLENGTH);
end;

function TWinampPlaylist.GetItem(Index: Integer): TTrack;
begin
{$ifndef NO_EXCEPTIONS}
  if (Index < 0) or (Index >= Length) then
    raise ERangeError.CreateFmt(SInvalidTrackIndex, [Index]);
{$endif}
  FTrack.Index:= Index;
  Result:= FTrack;
end;

function TWinampPlaylist.GetPlaylistFile: String;
begin
  Result:= FPlaylistFile;

  MessageBox(0, PChar(IntToStr(FWinamp.SendMsg(WM_USER+2, 0, 0))), '', mb_ok);
end;

function TWinampPlaylist.GetPosition: Integer;
begin
  Result:= FWinamp.SendMsg(WM_WA_IPC, 0, IPC_GETLISTPOS);
end;

procedure TWinampPlaylist.SetPosition(Value: Integer);
begin
{$ifndef NO_EXCEPTIONS}
  if (Value < 0) or (Value >= Length) then
    raise ERangeError.CreateFmt(SInvalidTrackIndex, [Value]);
{$endif}
  FWinamp.SendMsg(WM_WA_IPC, Value, IPC_SETPLAYLISTPOS);
end;

procedure TWinampPlaylist.Add(const Path: String);
var
  cds: TCopyDataStruct;
begin
  cds.dwData:= IPC_ENQUEUEFILE;
  cds.lpData:= PChar(Path);
  cds.cbData:= System.Length(Path) + 1;
  FWinamp.SendMsg(WM_COPYDATA, 0, Integer(@cds));
end;

procedure TWinampPlaylist.Clear;
begin
  FWinamp.SendMsg(WM_WA_IPC, 0, IPC_DELETE);
end;

procedure TWinampPlaylist.Load(const Path: String);
begin
  Clear;
  Add(Path);
end;

procedure TWinampPlaylist.RemoveDeadFiles;
begin
{ TODO : 1 }
end;

procedure TWinampPlaylist.Save;
begin
{ TODO : 2 }
end;

procedure TWinampPlaylist.Save(const PlaylistFile: String);
begin
{ TODO : 3 }
end;

procedure TWinampPlaylist.Sort(SortMode: TSortMode);
begin
  case SortMode of
    smTitle:
      FWinamp.SendMsg(WM_COMMAND, WINAMP_SORT_BY_TITLE, 0);
    smFileName:
      FWinamp.SendMsg(WM_COMMAND, WINAMP_SORT_BY_FILENAME, 0);
    smFullPath:
      FWinamp.SendMsg(WM_COMMAND, WINAMP_SORT_BY_PATH, 0);
    smReverse:
      FWinamp.SendMsg(WM_COMMAND, WINAMP_SORT_REVERSE, 0);
    smRandomize:
      FWinamp.SendMsg(WM_COMMAND, WINAMP_SORT_RANDOMIZE, 0);
  end
end;

{ TWinampVersion }

constructor TWinampVersion.Create(AWinamp: TWinamp);
var
  I: Integer;
begin
  I:= AWinamp.SendMsg(WM_WA_IPC, 0, IPC_GETVERSION);
  FMajor:= (I and $F000) div $1000;

  I:= AWinamp.SendMsg(WM_WA_IPC, 0, IPC_GETVERSION);
  if FMajor = 1 then
    FMinor:= ((I and $0F00) div (16 * 16 * 16)) * 10 + (I and $000F)
  else
    FMinor:= ((I and $0F00) div 256) * 10 + (I and $000F);
end;

{ TWinamp }

constructor TWinamp.Create(hwndWinamp: THandle);
begin
  CheckWinamp(hwndWinamp);
  FhwndWinamp:= hwndWinamp;
  FVolume:= TWinampVolume.Create(Self);
  FCurrentTrack:= TWinampTrack.Create(Self);
  FPlaylist:= TWinampPlaylist.Create(Self);
  FVersion:= TWinampVersion.Create(Self);
{ Subclassing Winamp window }
  FOldWndProc:= Pointer(SetWindowLong(FhwndWinamp,
      GWL_WNDPROC,
      Integer(MakeObjectInstance(WndProc))));
end;

destructor TWinamp.Destroy;
begin
  SetWindowLong(FhwndWinamp, GWL_WNDPROC, Integer(FOldWndProc));
  FPlaylist.Free;
  FCurrentTrack.Free;
  FVolume.Free;
end;

function TWinamp.SendMsg(Msg: Cardinal; wParam, lParam: Integer): Integer;
begin
  Result:= SendMessage(FhwndWinamp, Msg, wParam, lParam);
end;

procedure TWinamp.CallOldWndProc(var Message: TMessage);
begin
  With Message do
    Result:= CallWindowProc(FOldWndProc,
        FhwndWinamp,
        Msg,
        wParam,
        lParam);
end;

{$WRITEABLECONST ON}
procedure TWinamp.WndProc(var Message: TMessage);
var
  OldValue: Integer;
  DontPass: Boolean;
    { if True message will not pass to default
      winamp window procedure }
  State1, State2: TPlayState;
const
  ByUser: Boolean = True;
    { flag }
  OldState: TPlayState = psStop;
begin
{ TODO : subclassing }

{ Before call original winamp window procedure }

  DontPass:= False;

  case Message.Msg of
    WM_COMMAND, WM_SYSCOMMAND:
    begin
      if Assigned(FOnBeforeCommand) then
      begin
        FOnBeforeCommand(Message.WParamLo, DontPass);
        Message.Result:= 0;
      end;

      case Message.WParamLo of
      { Current track change checks }
        WINAMP_PREVBUTTON, WINAMP_NEXTBUTTON:
          OldValue:= FPlaylist.Position;
            
      end;
    end; { WM_COMMAND, WM_SYSCOMMAND }

    WM_WA_STARTNEXT:
      ByUser:= False;
        { reset ByUser flag }

  end; { case Message.Msg }

{ Call default winamp window procedure }
  if not DontPass then
    CallOldWndProc(Message);

{ After call of the default winamp window procedure }
  case Message.Msg of
    WM_COMMAND, WM_SYSCOMMAND:
    begin
      if (Assigned(FOnAfterCommand)) and (not DontPass) then
        FOnAfterCommand(Message.WParamLo);

      case Message.wParamLo of
        WINAMP_BUTTON1:
          if Assigned(FOnPrev) then
            if (FPlaylist.Length > 0) and
               (OldValue <> FPlaylist.Position) then
              FOnPrev();

        WINAMP_BUTTON5:
          if Assigned(FOnNext) then
            if (FPlaylist.Length > 0) and
               (OldValue <> FPlaylist.Position) then
              FOnNext(True);
      end; { case Message.wParamLo... }

    end; { WM_COMMAND, WM_SYSCOMMAND }

    WM_WA_STARTNEXT:
    begin
      if Assigned(FOnNext) then
        if State = psPlay then
          FOnNext(ByUser);
    end;

    WM_WA_IPC:
    begin
    { Changed play state }
      if Message.lParam = $25b then
      begin
        State1:= State;
        if OldState <> State1 then
        begin
          State2:= OldState;
          OldState:= State1;

          if Assigned(OnStateChange) then
            OnStateChange(State1, State2, ByUser);

          case State1 of
            psPlay:
              if Assigned(OnPlay) then
                OnPlay();
            psPause:
              if Assigned(OnPause) then
                OnPause();
            psStop:
              if Assigned(OnStop) then
                OnStop(ByUser);
          end;

          ByUser:= True;
            { set ByUser flag }
        end;
      end;
    end; { WM_WA_IPC }
  end; { case Message.Msg... }
end;
{$WRITEABLECONST OFF}

function TWinamp.GetMode: TPlayMode;
begin
  Result:= [];
  if SendMessage(FhwndWinamp, WM_WA_IPC, 0, IPC_GET_REPEAT) = 1 then
    Include(Result, pmRepeat);
  if SendMessage(FhwndWinamp, WM_WA_IPC, 0, IPC_GET_SHUFFLE) = 1 then
    Include(Result, pmShuffle);
end;

procedure TWinamp.SetMode(const Value: TPlayMode);
begin
  if pmRepeat in Value then
    SendMessage(FhwndWinamp, WM_WA_IPC, 1, IPC_SET_REPEAT)
  else
    SendMessage(FhwndWinamp, WM_WA_IPC, 0, IPC_SET_REPEAT);

  if pmShuffle in Value then
    SendMessage(FhwndWinamp, WM_WA_IPC, 1, IPC_SET_SHUFFLE)
  else
    SendMessage(FhwndWinamp, WM_WA_IPC, 0, IPC_SET_SHUFFLE);
end;

function TWinamp.GetState: TPlayState;
begin
  case SendMessage(FhwndWinamp, WM_WA_IPC, 0, IPC_ISPLAYING) of
    0: Result:= psStop;
    1: Result:= psPlay;
    3: Result:= psPause;
  else
    Result:= psNone;
  end;
end;

procedure TWinamp.Prev;
begin
  SendMessage(FhwndWinamp, WM_COMMAND, WINAMP_BUTTON1, 0);
end;

procedure TWinamp.Play;
begin
  SendMessage(FhwndWinamp, WM_COMMAND, WINAMP_BUTTON2, 0);
end;

procedure TWinamp.Play(const Path: String);
begin
  Playlist.Load(Path);
  SendMessage(FhwndWinamp, WM_WA_IPC, 0, IPC_STARTPLAY);
end;

procedure TWinamp.Pause;
begin
  SendMessage(FhwndWinamp, WM_COMMAND, WINAMP_BUTTON3, 0);
end;

procedure TWinamp.Stop;
begin
  SendMessage(FhwndWinamp, WM_COMMAND, WINAMP_BUTTON4, 0);
end;

procedure TWinamp.Next;
begin
  SendMessage(FhwndWinamp, WM_COMMAND, WINAMP_BUTTON5, 0);
end;

procedure TWinamp.Fastforward;
begin
  SendMessage(FhwndWinamp, WM_COMMAND, WINAMP_BUTTON5_SHIFT, 0);
end;

procedure TWinamp.Rewind;
begin
  SendMessage(FhwndWinamp, WM_COMMAND, WINAMP_BUTTON1_SHIFT, 0);
end;

procedure CheckWinamp(hwndWinamp: THandle);
var
  PID: Cardinal;
begin
{$ifndef NO_EXCEPTIONS}
{ Check that we are in the winamp plug-in }
  GetWindowThreadProcessId(hwndWinamp, PID);
  if PID <> GetCurrentProcessId then
    raise EWinampPlaylist.Create(SNeedWinampPlugin);
{$endif}
end;

end.
	  
Слава Антонов © 2002 — August 13, 2008
Индекс цитирования
Hosted by uCoz