editor/bass-sys/win/bass24/delphi/multi/Multi.dpr
2021-01-07 21:37:50 -06:00

308 lines
9.3 KiB
ObjectPascal

{
BASS multiple output example
Copyright (c) 2001-2008 Un4seen Developments Ltd.
C++ to Delphi with use API adapted by Evgeny Melnikov
Required Delphi 7 or above
}
program Multi;
uses
Windows, Messages, CommCtrl, CommDlg, Bass in '..\Bass.pas';
const
szFiles = 'streamable files'#0'*.mp3;*.mp2;*.mp1;*.ogg;*.wav;*.aif'#0 +
'All files'#0'*.*'#0#0;
szOpen = 'Select a file to play';
var
win : HWND;
ofn : OPENFILENAME;
FileName : array[0..MAX_PATH - 1] of Char;
outdev : array[0..1] of DWORD; // output devices
latency : array[0..1] of DWORD; // latencies
chan : array[0..1] of HSTREAM; // the streams
{$R 'multi.res' 'multi.rc'}
//------------------ Auxiliary functions -------------------
function Format(const Format : String; const Args : array of const ) : String;
var
I : Integer;
FormatBuffer : array[0..High(Word)] of Char;
Arr, Arr1 : PDWORD;
PP : PDWORD;
begin
Arr := NIL;
if High(Args) >= 0 then
GetMem(Arr, (High(Args) + 1) * SizeOf(Pointer));
Arr1 := Arr;
for I := 0 to High(Args) do
begin
PP := @Args[I];
PP := Pointer(PP^);
Arr1^ := DWORD(PP);
inc(Arr1);
end;
I := wvsprintf(@FormatBuffer[0], PChar(Format), PChar(Arr));
SetLength(Result, I);
Result := FormatBuffer;
if Arr <> NIL then
FreeMem(Arr);
end;
//---------------------------------------------------------
// display error messages
procedure Error(const es : String);
begin
MessageBox(win, PChar(Format('%s' + #13#10 + '(error code: %d)', [es, BASS_ErrorGetCode])), 'Error', MB_OK or MB_ICONERROR);
end;
//---------------------------------------------------------
// Cloning DSP function
procedure CloneDSP(Handle : HDSP; channel : DWORD; buffer : Pointer; Length : DWORD; user : Pointer); stdcall;
begin
BASS_StreamPutData(HSTREAM(user), buffer, Length); // user = clone
end;
//---------------------------------------------------------
procedure InitApp(Wnd : HWND);
var
info : BASS_INFO;
di : BASS_DEVICEINFO;
St : AnsiString;
begin
win := Wnd;
// get a playable file
FillChar(ofn, SizeOf(OPENFILENAME), 0);
FillChar(FileName, SizeOf(FileName), 0);
ofn.lStructSize := SizeOf(OPENFILENAME);
ofn.hwndOwner := Wnd;
ofn.hInstance := hInstance;
ofn.lpstrTitle := szOpen;
ofn.lpstrFile := FileName;
ofn.lpstrFilter := szFiles;
ofn.nMaxFile := MAX_PATH;
ofn.Flags := OFN_FILEMUSTEXIST or OFN_HIDEREADONLY or OFN_EXPLORER;
// initialize output devices
if not BASS_Init(outdev[0], 44100, BASS_DEVICE_LATENCY, Wnd, NIL) then
begin
Error('Can''t initialize device 1');
DestroyWindow(Wnd);
end;
BASS_GetInfo(info);
latency[0] := info.latency;
if not BASS_Init(outdev[1], 44100, BASS_DEVICE_LATENCY, Wnd, NIL) then
begin
Error('Can''t initialize device 2');
DestroyWindow(Wnd);
end;
BASS_GetInfo(info);
latency[1] := info.latency;
BASS_GetDeviceInfo(outdev[0], di);
St := di.name;
SendDlgItemMessage(Wnd, 20, WM_SETTEXT, 0, Integer(PChar(String(St))));
BASS_GetDeviceInfo(outdev[1], di);
St := di.name;
SendDlgItemMessage(Wnd, 21, WM_SETTEXT, 0, Integer(PChar(String(St))));
end;
//---------------------------------------------------------
function DialogProc(hWndDlg : HWND; Msg, wParam, lParam : Longint) : Integer; stdcall;
type
TBuf = array of Byte;
var
info : BASS_CHANNELINFO;
devn,
devnx : Integer;
c, d : DWORD;
temp : HSTREAM;
St : String;
Buf : TBuf;
Buf1 : array[0..MAX_PATH - 1] of AnsiChar;
Buf2 : array[0..MAX_PATH - 1] of AnsiChar;
begin
Result := 0;
case Msg of
WM_INITDIALOG :
begin
InitApp(hWndDlg);
Result := 1;
Exit;
end;
WM_COMMAND :
case LOWORD(wParam) of
10, 11 : // open a file to play on device #1 or device #2
begin
devn := LOWORD(wParam) - 10;
if GetOpenFileName(ofn) then
begin
St := FileName;
BASS_StreamFree(chan[devn]); // free old stream
BASS_SetDevice(outdev[devn]); // set the device to create stream on
chan[devn] := BASS_StreamCreateFile(False, PChar(St), 0, 0, BASS_SAMPLE_LOOP {$IFDEF UNICODE} or BASS_UNICODE {$ENDIF});
if chan[devn] = 0 then
begin
SendDlgItemMessage(hWndDlg, 10 + devn, WM_SETTEXT, 0, Integer(PChar('click here to open a file...')));
Error('Can''t play the file');
Exit;
end;
BASS_ChannelPlay(chan[devn], False); // play new stream
SendDlgItemMessage(hWndDlg, 10 + devn, WM_SETTEXT, 0, Integer(PChar(St)));
end;
end;
15, 16 : // clone on device #1 or device #2
begin
devn := LOWORD(wParam) - 15;
devnx := devn xor 1;
if not BASS_ChannelGetInfo(chan[devnx], info) then
begin
Error('Nothing to clone');
Exit;
end;
BASS_StreamFree(chan[devn]); // free old stream
BASS_SetDevice(outdev[devn]); // set the device to create stream on
chan[devn] := BASS_StreamCreate(info.freq, info.chans, info.flags, STREAMPROC_PUSH, NIL);
if chan[devn] = 0 then
begin // create a "push" stream
SendDlgItemMessage(hWndDlg, 10 + devn, WM_SETTEXT, 0, Integer(PChar('click here to open a file...')));
Error('Can''t create clone');
Exit;
end;
BASS_ChannelLock(chan[devnx], True); // lock source stream to synchonise buffer contents
BASS_ChannelSetDSP(chan[devnx], @CloneDSP, Pointer(chan[devn]), 0); // set DSP to feed data to clone
// copy buffered data to clone
d := BASS_ChannelSeconds2Bytes(chan[devn], latency[devn] / 1000); // playback delay
c := BASS_ChannelGetData(chan[devnx], NIL, BASS_DATA_AVAILABLE);
SetLength(Buf, c);
c := BASS_ChannelGetData(chan[devnx], buf, c);
if c > d then
BASS_StreamPutData(chan[devn], Pointer(DWORD(buf) + d), c - d);
BASS_ChannelLock(chan[devnx], False); // unlock source stream
BASS_ChannelPlay(chan[devn], False); // play clone
SendDlgItemMessage(hWndDlg, 10 + devn, WM_SETTEXT, 0, Integer(PChar('clone')));
end;
30 : // swap channel devices
begin
// swap handles
temp := chan[0];
chan[0] := chan[1];
chan[1] := temp;
// swap text
SendDlgItemMessage(hWndDlg, 10, WM_GETTEXT, SizeOf(Buf1), Integer(@Buf1[0]));
SendDlgItemMessage(hWndDlg, 11, WM_GETTEXT, SizeOf(Buf2), Integer(@Buf2[0]));
SendDlgItemMessage(hWndDlg, 10, WM_SETTEXT, 0, Integer(@Buf2[0]));
SendDlgItemMessage(hWndDlg, 11, WM_SETTEXT, 0, Integer(@Buf1[0]));
// update the channel devices
BASS_ChannelSetDevice(chan[0], outdev[0]);
BASS_ChannelSetDevice(chan[1], outdev[1]);
end;
IDCANCEL : DestroyWindow(hWndDlg);
end;
WM_CLOSE : DestroyWindow(hWndDlg);
WM_DESTROY :
begin
// release both devices
BASS_SetDevice(outdev[0]);
BASS_Free;
BASS_SetDevice(outdev[1]);
BASS_Free;
end;
end;
end;
//---------------------------------------------------------
// Simple device selector dialog stuff begins here
function DeviceDialogProc(hWndDlg : HWND; Msg, wParam, lParam : Longint) : Integer; stdcall;
var
i, idx,
Device : Integer;
di : BASS_DEVICEINFO;
St : String;
St1 : AnsiString;
begin
Result := 0;
case Msg of
WM_INITDIALOG :
begin
St := Format('Select output device #%d', [lParam]);
SetWindowText(hWndDlg, PChar(St));
// device 1 = 1st real device
i := 1;
while BASS_GetDeviceInfo(I, di) do
begin
if di.flags and BASS_DEVICE_ENABLED = BASS_DEVICE_ENABLED then // enabled, so add it...
begin
St1 := di.name;
idx := SendDlgItemMessage(hWndDlg, 10, LB_ADDSTRING, 0, Integer(PChar(String(St1))));
SendDlgItemMessage(hWndDlg, 10, LB_SETITEMDATA, idx, i); // store device #
end;
inc(i);
end;
SendDlgItemMessage(hWndDlg, 10, LB_SETCURSEL, 0, 0);
Result := 1;
Exit;
end;
WM_COMMAND :
case LOWORD(wParam) of
10 :
if HIWORD(wParam) <> LBN_DBLCLK then
Exit;
IDOK :
begin
Device := SendDlgItemMessage(hWndDlg, 10, LB_GETCURSEL, 0, 0);
Device := SendDlgItemMessage(hWndDlg, 10, LB_GETITEMDATA, Device, 0); // get device #
EndDialog(hWndDlg, Device);
end;
end;
end;
end;
// Device selector stuff ends here
//---------------------------------------------------------
begin
// check the correct BASS was loaded
if HIWORD(BASS_GetVersion) <> BASSVERSION then
begin
MessageBox(0, 'An incorrect version of BASS.DLL was loaded', 'Error', MB_OK or MB_ICONERROR);
Halt(BASS_ERROR_VERSION);
end;
// Let the user choose the output devices
outdev[0] := DialogBoxParam(hInstance, MakeIntResource(2000), 0, @DeviceDialogproc, 1);
outdev[1] := DialogBoxParam(hInstance, MakeIntResource(2000), 0, @DeviceDialogproc, 2);
// display the window
DialogBox(hInstance, MakeIntResource(1000), 0, @DialogProc);
end.