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