Solved

Winamp plugin

Posted on 1998-12-17
3
1,065 Views
Last Modified: 2013-11-18
How to make a Winamp plugin from Delphi, I want to have some Delphi code compiled to a Winamp plugin
0
Comment
Question by:koger
  • 2
3 Comments
 
LVL 3

Accepted Solution

by:
williams2 earned 50 total points
Comment Utility
This is an example I found some time ago, but I have problems finding the URL again, so I'll just have to do some copy and paste:

Author:
This project is a plugin for WinAmp 1.6+ - Now you can also write WinAmp
plugins in Borland Delphi and not be restricted to only using C/C++.
If you compile the code you should also see that the resulting .DLL
(compiled with Delphi 2.0) is about 40% smaller than the C/C++ version
(18432 bytes for the Delphi .DLL versus 31232 bytes for the C/C++ .DLL).

It is a direct Delphi 2.0/3.0 port of the VIS_TEST.DLL which was created with
Visual C++ 4.2/5.0 with source contained in file VIS_SRC.ZIP available on
WinAmp's Web Site at http://winamp.lh.net.
Simply copy the file VIS_TEST.DLL (you may rename it if you wish) to the
directory where WINAMP is installed and then select the plugin from
WinAmp's "Plugins" Page.

Hope you find this useful
N.M. ISMAIL - 16/11/97




Project file should look like this (Go open->new->DLL), then go add 3 extra units. Now go save project as Vis_Test, unit1 as vis.pas, unit2 as svis.pas and unit3 as mysysut1.pas. here you are ready to cut'n'paste the following material:

library Vis_Test;
uses
  Vis in 'vis.PAS',
  SVis in 'svis.pas',
  MySysUtl in 'mysysutl.pas';

exports
  winampVisGetHeader;
end.
________________________________________________________________
// Winamp test visualization library v1.0
// Copyright (C) 1997, Justin Frankel/Nullsoft
// Feel free to base any plugins on this "framework"...
// Delphi conversion by N.M. ISMAIL - 16/11/97 (Requires Delphi 2.0 or higher)

// Note use of MySysUtl unit instead of SysUtils.  We only needed two
// PChar functions namely StrLen and StrCat, so it was better to put
// them into their own unit! (reduces .DLL size by about 20KB).
unit SVis; interface
uses MySysUtl,{SysUtils,} Windows, Messages, Vis;

const szAppName : PChar = 'SimpleVis'; // Our window class, etc

// configuration declarations
const config_x : integer = 50;
      config_y : integer = 50;      // screen X position and Y position, respectively

procedure config_read(this_mod : PwinampVisModule); // reads the configuration
procedure config_write(this_mod : PwinampVisModule); // writes the configuration
procedure config_getinifn(this_mod : PwinampVisModule; ini_file : PChar); // makes the .ini file filename


// returns a winampVisModule when requested. Used in hdr, below
function getModule(which : integer) : PwinampVisModule; cdecl;

// "member" functions
procedure config(this_mod : PwinampVisModule); cdecl; // configuration dialog
function init(this_mod : PwinampVisModule):integer; cdecl;  // initialization for module
function render1(this_mod : PwinampVisModule):integer; cdecl;  // rendering for module 1
function render2(this_mod : PwinampVisModule):integer; cdecl;  // rendering for module 2
function render3(this_mod : PwinampVisModule):integer; cdecl;  // rendering for module 3
procedure quit(this_mod : PwinampVisModule); cdecl;  // deinitialization for module

// our window procedure
//LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
function WndProc(hwnd:HWND; message:UINT; wParam:WPARAM; lParam:LPARAM):LRESULT; cdecl;

var
  hMainWnd : HWND; // main window handle

// Double buffering data
  memDC : HDC;     // memory device context
  memBM,           // memory bitmap (for memDC)
  oldBM : HBITMAP; // old bitmap (from memDC)


// Module header, includes version, description, and address of the module retriever function
const
  hdr  : TwinampVisHeader = (version:VIS_HDRVER; description:'Nullsoft Test Visualization Library v1.0'; getModule:getModule);

// first module (oscilliscope)
  mod1 : TwinampVisModule =
   (
    description:'Oscilliscope';
    hwndParent:0; { NULL }     // hwndParent
    hDllInstance:0; { NULL }   // hDllInstance
    sRate:0;                   // sRate
    nCh:0;                     // nCh
    latencyMs:25;              // latencyMS
    delayMS:25;                // delayMS
    spectrumNch:0;             // spectrumNch
    waveformNch:2;             // waveformNch
//  spectrumData:( 0, );       // spectrumData
//  waveformData:( 0, );       // waveformData
    Config:config;
    Init:init;
    Render:render1;
    Quit:quit
   );

// second module (spectrum analyser)
  mod2 : TwinampVisModule =
   (
    description:'Spectrum Analyser';
    hwndParent:0; { NULL }     // hwndParent
    hDllInstance:0; { NULL }   // hDllInstance
    sRate:0;                   // sRate
    nCh:0;                     // nCh
    latencyMs:25;              // latencyMS
    delayMS:25;                // delayMS
    spectrumNch:2;             // spectrumNch
    waveformNch:0;             // waveformNch
//  spectrumData:( 0, );       // spectrumData
//  waveformData:( 0, );       // waveformData
    Config:config;
    Init:init;
    Render:render2;
    Quit:quit
   );

// third module (VU meter)
  mod3 : TwinampVisModule =
   (
    description:'VU Meter';
    hwndParent:0; { NULL }     // hwndParent
    hDllInstance:0; { NULL }   // hDllInstance
    sRate:0;                   // sRate
    nCh:0;                     // nCh
    latencyMs:25;              // latencyMS
    delayMS:25;                // delayMS
    spectrumNch:0;             // spectrumNch
    waveformNch:2;             // waveformNch
//  spectrumData:( 0, );       // spectrumData
//  waveformData:( 0, );       // waveformData
    Config:config;
    Init:init;
    Render:render3;
    Quit:quit
   );

// this is the only exported symbol. returns our main header.
function winampVisGetHeader : PwinampVisHeader; cdecl; export;

implementation

// this is the only exported symbol. returns our main header.
function winampVisGetHeader : PwinampVisHeader;
begin
  Result := @hdr;
end; { winampVisGetHeader }

// getmodule routine from the main header. Returns NULL if an invalid module was requested,
// otherwise returns either mod1, mod2 or mod3 depending on 'which'.
function getModule(which : integer) : PwinampVisModule;
begin
  case which of
    0 : Result := @mod1;
    1 : Result := @mod2;
    2 : Result := @mod3;
   else Result := nil;
  end;
end; { getModule }

// configuration. Passed this_mod, as a "this" parameter. Allows you to make one configuration
// function that shares code for all your modules (you don't HAVE to use it though, you can make
// config1(), config2(), etc...)
procedure config(this_mod : PwinampVisModule);
begin
  MessageBox(this_mod^.hwndParent,'This module is Copyright(C) 1997, Justin Frankel/Nullsoft'#13+
                                  'Delphi conversion by N.M. ISMAIL - 16/11/97'#13+
                                  '-- This is just a demonstration module, it really isn''t'#13+
                                  '   supposed to be enjoyable --','Configuration',MB_OK);
end; { config }

// initialization. Registers our window class, creates our window, etc. Again, this one works for
// both modules, but you could make init1() and init2()...
// returns 0 on success, 1 on failure.
function init(this_mod : PwinampVisModule) : integer;
var width, height : integer;
    wc : TWNDCLASS;
    r  : TRECT;
begin
  if (this_mod = @mod3) then width := 256 else width := 288;  // width and height are the same for mod1 and mod2,
  if (this_mod = @mod3) then height := 32 else height := 256; // but mod3 is shaped differently

  config_read(this_mod);

  // Register our window class
  {memset}fillchar(wc,sizeof(wc),0);
  wc.lpfnWndProc := @WndProc;                 // our window procedure
  wc.hInstance := this_mod^.hDllInstance;     // hInstance of DLL
  wc.lpszClassName := szAppName;              // our window class name

  if (Windows.RegisterClass(wc) = 0) then
    begin
      MessageBox(this_mod^.hwndParent,'Error registering window class','blah',MB_OK);
      Result:=1;
      exit;
    end;

  hMainWnd := CreateWindowEx(
          WS_EX_TOOLWINDOW or WS_EX_APPWINDOW,            // these exstyles put a nice small frame,
                                                          // but also a button in the taskbar
          szAppName,                                      // our window class name
          this_mod^.description,                          // use description for a window title
          WS_VISIBLE or WS_SYSMENU,                       // make the window visible with a close button
          config_x,config_y,                              // screen position (read from config)
          width,height,                                   // width & height of window (need to adjust client area later)
          this_mod^.hwndParent,                           // parent window (winamp main window)
          0, { NULL }                                     // no menu
          this_mod^.hDllInstance,                         // hInstance of DLL
          nil {0}); // no window creation data

  if (hMainWnd = 0) then
    begin
      MessageBox(this_mod^.hwndParent,'Error creating window','blah',MB_OK);
      Result:=1;
      exit;
    end;


  SetWindowLong(hMainWnd,GWL_USERDATA,longint(this_mod)); // set our user data to a "this" pointer

  // adjust size of window to make the client area exactly width x height
  GetClientRect(hMainWnd,r);
  SetWindowPos(hMainWnd,0,0,0,width*2-r.right,height*2-r.bottom,SWP_NOMOVE or SWP_NOZORDER);

  // create our doublebuffer
  memDC := CreateCompatibleDC(0 {NULL});
  memBM := CreateCompatibleBitmap(memDC,width,height);
  oldBM := SelectObject(memDC,memBM);

  // show the window
  ShowWindow(hMainWnd,SW_SHOWNORMAL);
  Result := 0;
end; { init }

// render function for oscilliscope. Returns 0 if successful, 1 if visualization should end.
function render1(this_mod : PwinampVisModule) : integer;
var x, y : integer;
    _hdc : HDC;
begin
  // clear background
  Rectangle(memDC,0,0,288,256);

  // draw oscilliscope
  for y := 0 to this_mod^.nCh-1 do
    begin
      MoveToEx(memDC,0,(y*256) shr (this_mod^.nCh-1),nil);
      for x := 0 to 288-1 do
        begin
          LineTo(memDC,x,(y*256 + (ord(this_mod^.waveformData[y][x]) xor 128)) shr (this_mod^.nCh-1));
        end;
    end;

  // copy doublebuffer to window
  _hdc := GetDC(hMainWnd);
  BitBlt(_hdc,0,0,288,256,memDC,0,0,SRCCOPY);
  ReleaseDC(hMainWnd,_hdc);

  Result := 0;
end; { render1 }

// render function for analyser. Returns 0 if successful, 1 if visualization should end.
function render2(this_mod : PwinampVisModule) : integer;
var x, y : integer;
    _hdc : HDC;
begin
  // clear background
  Rectangle(memDC,0,0,288,256);

  // draw analyser
  for y := 0 to this_mod^.nCh-1 do
    begin
      for x := 0 to 288-1 do
        begin
          MoveToEx(memDC,x,(y*256+256) shr (this_mod^.nCh-1),nil);
          LineTo(memDC,x,(y*256 + 256 - ord(this_mod^.spectrumData[y][x])) shr (this_mod^.nCh-1));
        end;
    end;

  // copy doublebuffer to window
  _hdc := GetDC(hMainWnd);
  BitBlt(_hdc,0,0,288,256,memDC,0,0,SRCCOPY);
  ReleaseDC(hMainWnd,_hdc);

  Result := 0;
end; { render2 }

// render function for VU meter. Returns 0 if successful, 1 if visualization should end.
function render3(this_mod : PwinampVisModule):integer;
var x, y : integer;
    last, total : integer;
    _hdc : HDC;
begin
  // clear background
  Rectangle(memDC,0,0,256,32);

  // draw VU meter
  for y := 0 to 2-1 do
    begin
      last := ord(this_mod^.waveformData[y][0]);
      total := 0;
      for x := 1 to 576-1 do
        begin
          inc(total, abs(last - ord(this_mod^.waveformData[y][x])));
          last:=ord(this_mod^.waveformData[y][x]);
        end;

      total := total div 288;
      if (total > 127) then total := 127;
      if (y <> 0) then Rectangle(memDC,128,0,128+total,32)
        else Rectangle(memDC,128-total,0,128,32);
    end;

  // copy doublebuffer to window
  _hdc := GetDC(hMainWnd);
  BitBlt(_hdc,0,0,256,32,memDC,0,0,SRCCOPY);
  ReleaseDC(hMainWnd,_hdc);

  Result := 0;
end; { render3 }

// cleanup (opposite of init()). Destroys the window, unregisters the window class
procedure quit(this_mod : PwinampVisModule);
begin
  config_write(this_mod);       // write configuration
  SelectObject(memDC,oldBM);    // delete our doublebuffer
  DeleteObject(memDC);
  DeleteObject(memBM);
  DestroyWindow(hMainWnd);      // delete our window
  Windows.UnregisterClass(szAppName,this_mod^.hDllInstance); // unregister window class
end; { quit }


// window procedure for our window
//LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
function WndProc(hwnd : HWND; message : UINT; wParam : WPARAM; lParam : LPARAM) : LRESULT; { CALLBACK }
var ps   : TPAINTSTRUCT;
    r    : TRECT;
    _hdc : HDC;
    this_mod : PwinampVisModule;
begin
  case message of
    WM_CREATE,
    WM_ERASEBKGND:
              begin
                Result := 0;
                exit;
              end;
    WM_PAINT: begin
                // update from doublebuffer
                _hdc := BeginPaint(hwnd,ps);
                GetClientRect(hwnd,r);
                BitBlt(_hdc,0,0,r.right,r.bottom,memDC,0,0,SRCCOPY);
                EndPaint(hwnd,ps);

                Result := 0;
                exit;
              end;
    WM_DESTROY:
              begin
                PostQuitMessage(0);

                // Safety Check - There seems to be a bug in WinAmp 1.6Beta
                // which can cause window co-ordinates to become invalid!
                // so reread config_x and config_y window co-ordinates.
                // get config_x and config_y for configuration
                GetWindowRect(hMainWnd,r);
                config_x := r.left;
                config_y := r.top;

                Result := 0;
                exit;
              end;
    WM_KEYDOWN, // pass keyboard messages to main winamp window (for processing)
    WM_KEYUP: begin
                // get this_mod from our window's user data
                this_mod := PwinampVisModule(GetWindowLong(hwnd,GWL_USERDATA));
                PostMessage(this_mod^.hwndParent,message,wParam,lParam);

                Result := 0;
                exit;
              end;
    WM_MOVE:  begin
                // get config_x and config_y for configuration
                GetWindowRect(hMainWnd,r);
                config_x := r.left;
                config_y := r.top;

                Result := 0;
                exit;
              end;
  end;

  Result := DefWindowProc(hwnd,message,wParam,lParam);
end; { WndProc }


procedure config_getinifn(this_mod : PwinampVisModule; ini_file : PChar);
var p : PChar;
begin
  // makes a .ini file in the winamp directory named "plugin.ini"
  GetModuleFileName(this_mod^.hDllInstance,ini_file,MAX_PATH);
  p := ini_file+strlen(ini_file);
  while ((p >= ini_file) and (p^ <> '\')) do dec(p);
  inc(p);
  if (p >= ini_file) then p^ := #0;
  strcat(ini_file,'plugin.ini');
end; { config_getinifn }


procedure config_read(this_mod : PwinampVisModule);
var ini_file : array[0..MAX_PATH-1] of char;
begin
  config_getinifn(this_mod,ini_file);
  config_x := GetPrivateProfileInt(this_mod^.description,'Screen_x',config_x,ini_file);
  config_y := GetPrivateProfileInt(this_mod^.description,'Screen_y',config_y,ini_file);
end; { config_read }

procedure config_write(this_mod : PwinampVisModule);
var _string  : array[0..31] of char;
    ini_file : array[0..MAX_PATH-1] of char;
begin
  config_getinifn(this_mod,ini_file);

//  {wsprintf}StrFmt(_string,'%d',[config_x]);
  str(config_x, _string);  // More efficient than StrFmt(...)
  WritePrivateProfileString(this_mod^.description,'Screen_x',_string,ini_file);
//  {wsprintf}StrFmt(_string,'%d',[config_y]);
  str(config_y, _string);
  WritePrivateProfileString(this_mod^.description,'Screen_y',_string,ini_file);
end; { config_write }

end.
_________________________________________________________________
unit Vis; interface
uses Windows;

// notes:
// any window that remains in foreground should optimally pass
// keystrokes to the parent (winamp's) window, so that the user
// can still control it. unless escape is hit, or some option
// key specific to the vis is hit. As for storing configuration,
// Configuration data should be stored in <dll directory>\plugin.ini
// Look at the example plugin for a framework.

// ints are 32 bits, and structure members are aligned on the default 8 byte boundaries
// tested with VC++ 4.2 and 5.0
// Converted from original VC++ source to Delphi by N.M. ISMAIL - 16/11/97

type
  PwinampVisModule = ^TwinampVisModule;
  TwinampVisModule = record
    description : PChar;      // description of module
    hwndParent  : HWND;       // parent window (filled in by calling app)
    hDllInstance : HINST;     // instance handle to this DLL (filled in by calling app)
    sRate : integer;            // sample rate (filled in by calling app)
    nCh   : integer;          // number of channels (filled in...)
    latencyMs : integer;      // latency from call of RenderFrame to actual drawing
                              // (calling app looks at this value when getting data)
    delayMs : integer;        // delay between calls in ms

    // the data is filled in according to the respective Nch entry
    spectrumNch : integer;
    waveformNch : integer;
    spectrumData : array[0..1,0..575] of char;
    waveformData : array[0..1,0..575] of char;

    Config : procedure(this_mod : PwinampVisModule); cdecl; // configuration dialog
    Init   : function(this_mod : PwinampVisModule) : integer; cdecl;  // 0 on success, creates window, etc
    Render : function(this_mod : PwinampVisModule) : integer; cdecl;  // returns 0 if successful, 1 if vis should end
    Quit   : procedure(this_mod : PwinampVisModule); cdecl;   // call when done

    userData : pointer; // user data, optional
  end;

  PwinampVisHeader = ^TwinampVisHeader;
  TwinampVisHeader = record
    version : integer;   // VID_HDRVER
    description : PChar; // description of library
    getModule : function(i : integer) : PwinampVisModule; cdecl;
  end;

// exported symbols
type winampVisGetHeaderType = function : PwinampVisHeader;

// version of current module ($101 == 1.01)
const VIS_HDRVER = $101;

implementation
end.
_________________________________________________________________
// A very small subset of the SysUtils unit from Delphi
unit MySysUtl; interface

// We only need the following two PChar routines
function StrLen(Str: PChar): Cardinal; assembler;
function StrCat(Dest, Source: PChar): PChar;

implementation

function StrLen(Str: PChar): Cardinal; assembler;
asm
        MOV     EDX,EDI
        MOV     EDI,EAX
        MOV     ECX,0FFFFFFFFH
        XOR     AL,AL
        REPNE   SCASB
        MOV     EAX,0FFFFFFFEH
        SUB     EAX,ECX
        MOV     EDI,EDX
end;

function StrEnd(Str: PChar): PChar; assembler;
asm
        MOV     EDX,EDI
        MOV     EDI,EAX
        MOV     ECX,0FFFFFFFFH
        XOR     AL,AL
        REPNE   SCASB
        LEA     EAX,[EDI-1]
        MOV     EDI,EDX
end;

function StrCopy(Dest, Source: PChar): PChar; assembler;
asm
        PUSH    EDI
        PUSH    ESI
        MOV     ESI,EAX
        MOV     EDI,EDX
        MOV     ECX,0FFFFFFFFH
        XOR     AL,AL
        REPNE   SCASB
        NOT     ECX
        MOV     EDI,ESI
        MOV     ESI,EDX
        MOV     EDX,ECX
        MOV     EAX,EDI
        SHR     ECX,2
        REP     MOVSD
        MOV     ECX,EDX
        AND     ECX,3
        REP     MOVSB
        POP     ESI
        POP     EDI
end;

function StrCat(Dest, Source: PChar): PChar;
begin
  StrCopy(StrEnd(Dest), Source);
  Result := Dest;
end;

end.

This was what I could come up with, I hope you find it usefull.

Regards,
Williams
0
 

Author Comment

by:koger
Comment Utility
Just what I needed, thanks

Do you have a description of WaveFormatDate, I would like to have the current Volumne Level and the max Volumne Level, for an equalizer.

0
 
LVL 3

Expert Comment

by:williams2
Comment Utility
No, I'm sorry, I do not know everything about this, this is the only thing I've got about it, but you should ask the developers at the winamp site, they are very nice people. There you could maybe get some more specific information.
The URL is: www.WinAMP.com

Regards,
Williams
0

Featured Post

Enabling OSINT in Activity Based Intelligence

Activity based intelligence (ABI) requires access to all available sources of data. Recorded Future allows analysts to observe structured data on the open, deep, and dark web.

Join & Write a Comment

Objective: - This article will help user in how to convert their numeric value become words. How to use 1. You can copy this code in your Unit as function 2. than you can perform your function by type this code The Code   (CODE) The Im…
Hello everybody This Article will show you how to validate number with TEdit control, What's the TEdit control? TEdit is a standard Windows edit control on a form, it allows to user to write, read and copy/paste single line of text. Usua…
The purpose of this video is to demonstrate how to set up the WordPress backend so that each page automatically generates a Mailchimp signup form in the sidebar. This will be demonstrated using a Windows 8 PC. Tools Used are Photoshop, Awesome…
Polish reports in Access so they look terrific. Take yourself to another level. Equations, Back Color, Alternate Back Color. Write easy VBA Code. Tighten space to use less pages. Launch report from a menu, considering criteria only when it is filled…

728 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

10 Experts available now in Live!

Get 1:1 Help Now