Delphi: load BASS DLL and play MP3

7.8k views Asked by At

I want to load a bass dll manually from somewhere and then play a mp3 file. How to do that? Delphi XE2.

My attempt doesn't work:

type
  QWORD = Int64;
  HSTREAM = LongWord;
type
  TBASS_ChannelPlay = function(handle: HSTREAM; restart: LongBool): LongBool; stdcall;

type
  TBASS_StreamCreateFile = function(mem: LongBool; f: Pointer;
    offset, length: QWORD; flags: LongWord): HSTREAM; stdcall;

procedure PlayMP3(FileName: string);
var
  BASS_ChannelPlay: TBASS_ChannelPlay;
  BASS_StreamCreateFile: TBASS_StreamCreateFile;
  DllHandle: THandle;
  MP3Stream: HSTREAM;
  DllPath:string;
  pFileName:pchar;
begin
  DllPath:= 'c:\dll\bass.dll';
  DllHandle := LoadLibrary(pchar(DLLPath));
  try
    if DllHandle = 0 then
      Exit;
    @BASS_StreamCreateFile := GetProcAddress(DllHandle,
      'BASS_StreamCreateFile');
    if @BASS_StreamCreateFile <> nil then
    begin
      pfilename:=pchar(FileName);
      MP3Stream := BASS_StreamCreateFile(false, Pfilename, 0, 0, 0);
      if MP3Stream <> 0 then
      begin
        @BASS_ChannelPlay := GetProcAddress(DllHandle, 'BASS_ChannelPlay');
        if @BASS_ChannelPlay <> nil then
          BASS_ChannelPlay(MP3Stream,false);
      end;
    end;
  finally
    FreeLibrary(DllHandle);
  end;
end;

But BASS_StreamCreateFile returns 0 all time :(

The code from their example (Bass Test) and Bass.pas:

type
  DWORD = LongWord;
  BOOL = LongBool;
  QWORD = Int64;
  HSTREAM = DWORD;

function BASS_StreamCreateFile(mem: BOOL; f: Pointer; offset, length: QWORD; flags: DWORD): HSTREAM; {$IFDEF WIN32}stdcall{$ELSE}cdecl{$ENDIF}; external bassdll;

strs: array[0..128] of HSTREAM;

procedure TForm1.Button15Click(Sender: TObject);
var
    f: PChar;
begin
    if not OpenDialog2.Execute then Exit;
    f := PChar(OpenDialog2.FileName);
    strs[strc] := BASS_StreamCreateFile(False, f, 0, 0, 0 {$IFDEF UNICODE} or BASS_UNICODE {$ENDIF});
    if strs[strc] <> 0 then
    begin
        ListBox3.Items.Add(OpenDialog2.FileName);
        Inc(strc);
    end
    else
        Error('Error creating stream!');
end;

Added

  MP3Stream := BASS_StreamCreateFile(false, Pfilename, 0, 0, 0);
  ShowMessage(SysErrorMessage(GetLastError));

GetLastError doesn't show any errors!

Added

There is BASS 2.4 Delphi unit (dynamic) too.

It's written:

How to install Copy DYNAMIC_BASS.PAS to the \LIB subdirectory of your Delphi path or your project dir Call Load_BASSDLL (eg. in FormCreate) to load BASS before using any functions, and
Unload_BASSDLL (eg. in FormDestory) to unload it when you're done.

NOTE: Delphi 2009 users should use the BASS_UNICODE flag where possible

I do:

 if Load_BASSDLL(DllPath) then
  begin
  FileName:='C:\Users\V\Desktop\cmd.mp3';
  MP3Stream:=BASS_StreamCreateFile(False, pchar(FileName), 0, 0, 0 {$IFDEF UNICODE} or BASS_UNICODE {$ENDIF});
    if MP3Stream <> 0 then
     BASS_ChannelPlay(MP3Stream, False);
     Unload_BASSDLL;
  end;

It still doesn't work :(

Added:

If to do

BASS_Init(-1, 44100, 0, Form1.Handle, nil);

then BASS_ChannelPlay(MP3Stream, False) returns true but I don't hear a sound:(

3

There are 3 answers

9
Ken White On BEST ANSWER

You're missing the call to BASS_Init. From the same demo you quoted from, in the FormCreate event:

// Initialize audio - default device, 44100hz, stereo, 16 bits
if not BASS_Init(-1, 44100, 0, Handle, nil) then
  Error('Error initializing audio!'); 

Also, look at the demo source's call:

BASS_StreamCreateFile(False, f, 0, 0, 
 0 {$IFDEF UNICODE} or BASS_UNICODE {$ENDIF});

Note what happens with the last parameter? The or BASS_UNICODE is required on D2009 and up, unless you're using PAnsiChar instead of PChar; Delphi's default string type is UnicodeString, and the default PChar is PWideChar.

You can use it with the {$IFDEF UNICODE} or without it; Delphi defines UNICODE, so if you use it your code will be backwards-compatible with earlier versions of Delphi (pre-D2009) as well.

You should also look at dynamic_bass.pas in the Bass\Delphi\dynamic folder. It has code to dynamically load the library and retrieve all the functions; if you don't want to use it directly, at least you could copy the function definitions and dynamic loading code you need from there instead.

0
paulsm4 On

Suggestion - just don't load the .dll manually. Forget "LoadDll" and ESPECIALLY forget all that grungy, low-level Win3.0 era "GetProcAddress()" nonsense.

Just declare an external, just like the working Bass sample code successfully does:

function BASS_StreamCreateFile(
           mem: BOOL; 
           f: Pointer; 
           offset, length: QWORD; 
           flags: DWORD): HSTREAM; 
{$IFDEF WIN32}stdcall{$ELSE}cdecl{$ENDIF}; external bassdll;

PS: If you really, really, REALLY insist on LoadDll () and friends, then:

  1. Remember that Delphi XE strings are now 16-bit Unicode

  2. Check GetLastError () to determine exactly WHY LoadDll returned a null handle.

PPS:

2
Charles Brown On