Hi…in this article i will try to teach you how to load Winamp DSP plugins and use it in your own multimedia application. I will use BASS (www.un4seen.com) as a sound engine in this tutorial, so you may had to download BASS api to get it work.
So lets begin.
For start we had to convert the Winamp SDK…This looks something like this:
type
TShortData = Array[ 0 ..20000] of Smallint;
PShortData = ^TShortData;
PWinampDSPModule = ^TWinampDSPModule;
TWinampDSPModule = record
Description : Pchar;
HwndParent : Hwnd;
hDLLinstance : Hinst;
Config : procedure(This_Mod : PWinAmpDSPModule); cdecl;
Init : function(This_Mod : PWinAmpDSPModule) : Integer; cdecl;
ModifySamples : function(This_Mod : PWinAmpDSPModule; Samples : PShortData;
NumSamples, Blocksize, nCh, sRate : Integer
) : Integer; cdecl;
Quit : procedure(This_Mod : PWinAmpDSPModule); cdecl;
UserData : Pointer;
end;
PWinAmpDSPHeader = ^TWinAmpDSPheader;
TWinAmpDSPHeader = record
Version : Integer;
Description : PChar;
GetModule : function(I : Integer) : PWinAmpDSPModule; cdecl;
end;
TGetHeader = function : PWinAmpDSPHeader; cdecl;
TGetModule = function(Which : Integer ) : PWinAmpDSPModule; cdecl;
TConfig = procedure(This_Mod : PWinAmpDSPModule ); cdecl;
TInit = function(This_Mod : PWinAmpDSPModule ) : Integer; cdecl;
TQuit = procedure(This_Mod : PWinAmpDSPModule ); cdecl;
TModifySamples = function(
This_Mod : PWinAmpDSPModule;
Samples : PShortData;
NumSamples, Blocksize, nCh, sRate : Integer
) : Integer; cdecl;
TShortData = Array[ 0 ..20000] of Smallint;
PShortData = ^TShortData;
PWinampDSPModule = ^TWinampDSPModule;
TWinampDSPModule = record
Description : Pchar;
HwndParent : Hwnd;
hDLLinstance : Hinst;
Config : procedure(This_Mod : PWinAmpDSPModule); cdecl;
Init : function(This_Mod : PWinAmpDSPModule) : Integer; cdecl;
ModifySamples : function(This_Mod : PWinAmpDSPModule; Samples : PShortData;
NumSamples, Blocksize, nCh, sRate : Integer
) : Integer; cdecl;
Quit : procedure(This_Mod : PWinAmpDSPModule); cdecl;
UserData : Pointer;
end;
PWinAmpDSPHeader = ^TWinAmpDSPheader;
TWinAmpDSPHeader = record
Version : Integer;
Description : PChar;
GetModule : function(I : Integer) : PWinAmpDSPModule; cdecl;
end;
TGetHeader = function : PWinAmpDSPHeader; cdecl;
TGetModule = function(Which : Integer ) : PWinAmpDSPModule; cdecl;
TConfig = procedure(This_Mod : PWinAmpDSPModule ); cdecl;
TInit = function(This_Mod : PWinAmpDSPModule ) : Integer; cdecl;
TQuit = procedure(This_Mod : PWinAmpDSPModule ); cdecl;
TModifySamples = function(
This_Mod : PWinAmpDSPModule;
Samples : PShortData;
NumSamples, Blocksize, nCh, sRate : Integer
) : Integer; cdecl;
After we have done this…we’ll go to load plugins function:
const
MCW_EM = DWORD($133F);
var
tel,a : Integer;
F : TSearchRec;
H : Hinst;
GetHeader : TGetHeader;
GetModule : TGetModule;
FuncPtr : TFarProc;
Hdr : PWinAmpDSPHeader;
Mdl : PWinAmpDSPModule;
Count : Integer;
nup : String;
info22: BASS_DEVICEINFO;
WinAmpSamplesForDSPBytesToAllocate: Integer;
begin
/////////////////////////////////WINAMP DSP/////////////////
///
///
Get8087CW;
Set8087CW($133f);
Set8087CW(MCW_EM); //disable floating exceptions
WinAmpSamplesForDSPBytesToAllocate := Round(96000 * // max sampling rate
4 * // 4 bytes for 32-bit sound
2 * // channels
// BASS will give at most 200ms worth of data in a single update,
// +5ms from me a value for safety reasons, no matter what
// the buffer length is.
(200 + 5) / 1000) *
2; // 2* is the max num samples that a Winamp DSP Plugin can return// 2* is the max num samples that a Winamp DSP Plugin can return
GetMem(WinAmpSamplesForDSP, WinAmpSamplesForDSPBytesToAllocate);
NrModules := 0;
if FindFirst(Path + 'DSP_*.DLL', 0, F) = 0 then
begin
repeat
nup := '';
for tel := 1 to length(F.Name ) do
nup := nup+Upcase(F.Name[Tel]);
if (nup 'DSP_MULTIPLE.DLL') and (nup 'DSP_MFX2.DLL') then
begin
H := LoadLibrary(Pchar(Path+F.Name));
FuncPtr := GetProcAddress(H, 'winampDSPGetHeader2');
if (H 0) and (FuncPtr = nil) then
begin
FreeLibrary(H);
H := 0;
end;
if (H 0) then
begin
@GetHeader := FuncPtr;
Hdr := GetHeader; {Get plugin-header}
dsp_list.Items.Add(Hdr^.Description);
GetModule := Hdr^.GetModule;
Count := 0;
repeat
Modules[NrModules].ModuleData := GetModule(Count);
Mdl := Modules[NrModules].ModuleData;
if Mdl nil then
begin
Modules[NrModules].ModuleNr := Count;
Modules[NrModules].Instance := H;
Modules[NrModules].DLLname := F.Name;
Modules[NrModules].DLLdescription := Hdr^.Description;
Modules[NrModules].Active := false;
// Modules[NrModules].ModuleData.HwndParent:=handle;
// Modules[NrModules].ModuleData.hDLLinstance:=H;
// Modules[NrModules].ModuleData.Init(Modules[NrModules].ModuleData);
//Modules[NrModules].Tree := Tree;
inc(NrModules);
end;
inc(Count);
until MDL = nil;
end;
end;
until (FindNext(F) 0);
end;
NrModules := NrModules - 1;
FindClose(F);
StartDSPPlugins;
//////////////BASS///////////////////////////
win := handle;
if (HIWORD(BASS_GetVersion) BASSVERSION) then
begin
MessageBox(0, 'An incorrect version of BASS.DLL was loaded', nil, MB_ICONERROR);
Halt;
end;
if (not BASS_Init(-1, 44100, 0, Handle, nil)) then
begin
Error('Can''t initialize device');
Halt;
end;
BASS_SetConfig(BASS_CONFIG_NET_BUFFER,trackbar1.Position);
BASS_SetConfig(BASS_CONFIG_NET_PLAYLIST, 1); // enable playlist processing
BASS_SetConfig(BASS_CONFIG_NET_PREBUF, 0); // minimize automatic pre-buffering, so we can do it (and display it) instead
BASS_SetConfigPtr(BASS_CONFIG_NET_PROXY, @proxy[0]); // setup proxy server location
MCW_EM = DWORD($133F);
var
tel,a : Integer;
F : TSearchRec;
H : Hinst;
GetHeader : TGetHeader;
GetModule : TGetModule;
FuncPtr : TFarProc;
Hdr : PWinAmpDSPHeader;
Mdl : PWinAmpDSPModule;
Count : Integer;
nup : String;
info22: BASS_DEVICEINFO;
WinAmpSamplesForDSPBytesToAllocate: Integer;
begin
/////////////////////////////////WINAMP DSP/////////////////
///
///
Get8087CW;
Set8087CW($133f);
Set8087CW(MCW_EM); //disable floating exceptions
WinAmpSamplesForDSPBytesToAllocate := Round(96000 * // max sampling rate
4 * // 4 bytes for 32-bit sound
2 * // channels
// BASS will give at most 200ms worth of data in a single update,
// +5ms from me a value for safety reasons, no matter what
// the buffer length is.
(200 + 5) / 1000) *
2; // 2* is the max num samples that a Winamp DSP Plugin can return// 2* is the max num samples that a Winamp DSP Plugin can return
GetMem(WinAmpSamplesForDSP, WinAmpSamplesForDSPBytesToAllocate);
NrModules := 0;
if FindFirst(Path + 'DSP_*.DLL', 0, F) = 0 then
begin
repeat
nup := '';
for tel := 1 to length(F.Name ) do
nup := nup+Upcase(F.Name[Tel]);
if (nup 'DSP_MULTIPLE.DLL') and (nup 'DSP_MFX2.DLL') then
begin
H := LoadLibrary(Pchar(Path+F.Name));
FuncPtr := GetProcAddress(H, 'winampDSPGetHeader2');
if (H 0) and (FuncPtr = nil) then
begin
FreeLibrary(H);
H := 0;
end;
if (H 0) then
begin
@GetHeader := FuncPtr;
Hdr := GetHeader; {Get plugin-header}
dsp_list.Items.Add(Hdr^.Description);
GetModule := Hdr^.GetModule;
Count := 0;
repeat
Modules[NrModules].ModuleData := GetModule(Count);
Mdl := Modules[NrModules].ModuleData;
if Mdl nil then
begin
Modules[NrModules].ModuleNr := Count;
Modules[NrModules].Instance := H;
Modules[NrModules].DLLname := F.Name;
Modules[NrModules].DLLdescription := Hdr^.Description;
Modules[NrModules].Active := false;
// Modules[NrModules].ModuleData.HwndParent:=handle;
// Modules[NrModules].ModuleData.hDLLinstance:=H;
// Modules[NrModules].ModuleData.Init(Modules[NrModules].ModuleData);
//Modules[NrModules].Tree := Tree;
inc(NrModules);
end;
inc(Count);
until MDL = nil;
end;
end;
until (FindNext(F) 0);
end;
NrModules := NrModules - 1;
FindClose(F);
StartDSPPlugins;
//////////////BASS///////////////////////////
win := handle;
if (HIWORD(BASS_GetVersion) BASSVERSION) then
begin
MessageBox(0, 'An incorrect version of BASS.DLL was loaded', nil, MB_ICONERROR);
Halt;
end;
if (not BASS_Init(-1, 44100, 0, Handle, nil)) then
begin
Error('Can''t initialize device');
Halt;
end;
BASS_SetConfig(BASS_CONFIG_NET_BUFFER,trackbar1.Position);
BASS_SetConfig(BASS_CONFIG_NET_PLAYLIST, 1); // enable playlist processing
BASS_SetConfig(BASS_CONFIG_NET_PREBUF, 0); // minimize automatic pre-buffering, so we can do it (and display it) instead
BASS_SetConfigPtr(BASS_CONFIG_NET_PROXY, @proxy[0]); // setup proxy server location
Now that we have load winamp dsp plugins let see how we can use them with BASS.
var
chan2: HSTREAM;
info: BASS_CHANNELINFO;
chan2:=BASS_StreamCreateFile(FALSE,pchar(filename), 0, 0,BASS_STREAM_PRESCAN);
BASS_ChannelGetInfo(chan2,info); //get channel info
BASS_ChannelSetDSP(chan2,@WinampDSPProc,nil,0);
//apply a dsp function that will send audio buffer to winamp dsp plugins
BASS_ChannelPlay(chan2, FALSE); //play the channel
chan2: HSTREAM;
info: BASS_CHANNELINFO;
chan2:=BASS_StreamCreateFile(FALSE,pchar(filename), 0, 0,BASS_STREAM_PRESCAN);
BASS_ChannelGetInfo(chan2,info); //get channel info
BASS_ChannelSetDSP(chan2,@WinampDSPProc,nil,0);
//apply a dsp function that will send audio buffer to winamp dsp plugins
BASS_ChannelPlay(chan2, FALSE); //play the channel
Now the DSP function:
function ConvertSamples_32BitTo16(InputSample: Single): Smallint;
var
i: Integer;
begin
i := Trunc(InputSample * 32768);
if i > 32767 then
i := 32767
else
if i < -32768 then
i := -32768;
Result := i;
end;
function fmod(a, b: Single): Single;
begin
Result := a - (b * Trunc(a / b));
end;
procedure DSPProcess16(DSPHandle: HDSP; Channel: DWORD; Buffer: PSample16; Length: DWORD; var User: DWORD); stdcall;
var
i: DWORD;
dmch: Smallint;
lch, rch: PSample16;
begin
try
i := 0;
lch := buffer;
rch := buffer;
Inc(rch);
while (i < length) do begin
dmch := Trunc(((0 - lch.data) + rch.data) / 2);
lch.data := dmch;
rch.data := dmch;
Inc(lch, 2);
Inc(rch, 2);
Inc(i, SizeOf(Smallint) * 2);
end;
except
//* This removes the DSP sync
end;
end;
function ConvertSamples_16BitTo32(InputSample: Smallint): Single;
begin
Result := InputSample / 32768;
end;
procedure WinampDSPProc(handle: HSYNC; channel: DWORD; buffer: Pointer; length, user: DWORD); stdcall;
var
SampleBuffer: PSingle;
WinampBuf: PSmallint;
SamplePos: DWORD;
LeftSample, RightSample: Smallint;
I: Integer;
NumberOfBufferSamples: Integer;
begin
SampleBuffer := Buffer;
WinampBuf := WinampSamplesForDSP;
SamplePos := 0;
while SamplePos < (length div 4) do
begin
// copy new buffer contents to the memory buffer
LeftSample := ConvertSamples_32BitTo16(SampleBuffer^);
WinampBuf^ := LeftSample;
Inc(WinampBuf);
Inc(SampleBuffer);
if Info.chans = 2 then
begin
RightSample := ConvertSamples_32BitTo16(SampleBuffer^);
WinampBuf^ := RightSample;
Inc(WinampBuf);
Inc(SampleBuffer);
end;
SamplePos := SamplePos + Info.chans; // use Info.chans to find how much samples were proccessed
end;
// Move(Buf^,WinampSamplesForDSP^, Length);
// for stereo samples the NumOfSamples = (Length div 2 BPS) div 2 Channels = Length div 4
NumberOfBufferSamples := Length div (Info.chans * 2);
for I := 0 to NrModules do
if modules[i].Active then
begin
modules[i].ModuleData.ModifySamples(modules[i].ModuleData,Buffer,NumberOfBufferSamples,16,info.chans,info.freq);
// SampleBuffer := Buffer;
// WinampBuf := WinampSamplesForDSP;
SamplePos := 0;
while SamplePos < (length div 4) do
begin
SamplePos := SamplePos + 2;
end;
end;
//Move(WinampSamplesForDSP^,Buf^,BytesToWriteToDSP);
end;
var
i: Integer;
begin
i := Trunc(InputSample * 32768);
if i > 32767 then
i := 32767
else
if i < -32768 then
i := -32768;
Result := i;
end;
function fmod(a, b: Single): Single;
begin
Result := a - (b * Trunc(a / b));
end;
procedure DSPProcess16(DSPHandle: HDSP; Channel: DWORD; Buffer: PSample16; Length: DWORD; var User: DWORD); stdcall;
var
i: DWORD;
dmch: Smallint;
lch, rch: PSample16;
begin
try
i := 0;
lch := buffer;
rch := buffer;
Inc(rch);
while (i < length) do begin
dmch := Trunc(((0 - lch.data) + rch.data) / 2);
lch.data := dmch;
rch.data := dmch;
Inc(lch, 2);
Inc(rch, 2);
Inc(i, SizeOf(Smallint) * 2);
end;
except
//* This removes the DSP sync
end;
end;
function ConvertSamples_16BitTo32(InputSample: Smallint): Single;
begin
Result := InputSample / 32768;
end;
procedure WinampDSPProc(handle: HSYNC; channel: DWORD; buffer: Pointer; length, user: DWORD); stdcall;
var
SampleBuffer: PSingle;
WinampBuf: PSmallint;
SamplePos: DWORD;
LeftSample, RightSample: Smallint;
I: Integer;
NumberOfBufferSamples: Integer;
begin
SampleBuffer := Buffer;
WinampBuf := WinampSamplesForDSP;
SamplePos := 0;
while SamplePos < (length div 4) do
begin
// copy new buffer contents to the memory buffer
LeftSample := ConvertSamples_32BitTo16(SampleBuffer^);
WinampBuf^ := LeftSample;
Inc(WinampBuf);
Inc(SampleBuffer);
if Info.chans = 2 then
begin
RightSample := ConvertSamples_32BitTo16(SampleBuffer^);
WinampBuf^ := RightSample;
Inc(WinampBuf);
Inc(SampleBuffer);
end;
SamplePos := SamplePos + Info.chans; // use Info.chans to find how much samples were proccessed
end;
// Move(Buf^,WinampSamplesForDSP^, Length);
// for stereo samples the NumOfSamples = (Length div 2 BPS) div 2 Channels = Length div 4
NumberOfBufferSamples := Length div (Info.chans * 2);
for I := 0 to NrModules do
if modules[i].Active then
begin
modules[i].ModuleData.ModifySamples(modules[i].ModuleData,Buffer,NumberOfBufferSamples,16,info.chans,info.freq);
// SampleBuffer := Buffer;
// WinampBuf := WinampSamplesForDSP;
SamplePos := 0;
while SamplePos < (length div 4) do
begin
SamplePos := SamplePos + 2;
end;
end;
//Move(WinampSamplesForDSP^,Buf^,BytesToWriteToDSP);
end;
We are done…Now you can use Winamp DSP plugins in your own application.

