
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@1830 8e941d3f-bd1b-0410-a28a-d453659cc2b4
436 lines
13 KiB
ObjectPascal
436 lines
13 KiB
ObjectPascal
{----------------------------------------------------------------------------
|
|
|
|
esdsound.pas
|
|
|
|
ESound non-visual component
|
|
for Lazarus
|
|
|
|
Written by Anthony Maro
|
|
with help from Andrew Johnson
|
|
|
|
NOTE: Included in gamepack for convenience, no credit to be taken from Tony.
|
|
|
|
Initial Release November 14, 2002
|
|
|
|
THIS COMPONENT IS FREEWARE - USE AS YOU WILL TO FURTHER LINUX
|
|
If you release sourcecode that uses this control, please credit me or leave this
|
|
header intact. If you release a compiled application that uses this code,
|
|
please credit me somewhere in a little bitty location so I can at least get
|
|
bragging rights!
|
|
|
|
This code is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
|
|
Component Use:
|
|
|
|
Drop it on your form. (Duh!)
|
|
At runtime, call the play method giving a filename (with full path) to play it.
|
|
|
|
If you want to have instant access to the sounds (instead of the short delay
|
|
the first time you play them) call the cachesample method as in:
|
|
|
|
ESDSound1.CacheSample('/usr/share/sounds/mysound.wav','shortname');
|
|
|
|
Then you can play the cached sample with:
|
|
|
|
ESDSound1.Play('shortname');
|
|
|
|
The shortname is CASE SENSITIVE!
|
|
|
|
When the control is enabled it keeps an open connection to the esound sound
|
|
server. Set it to disabled to close that connection and unload the esdlib
|
|
library. Set it to enabled to reload the library and connect to the sound
|
|
server
|
|
---------------------------------------------------------------------------}
|
|
|
|
unit esdsound;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils,
|
|
{$IFDEF UNIX}
|
|
+ oldlinux,
|
|
+{$ENDIF}
|
|
+ lresources;
|
|
+
|
|
+const
|
|
+ ESD_BUF_SIZE = 4 * 1024;
|
|
+ ESD_KEY_LEN = 16;
|
|
+ ESD_DEFAULT_PORT = 16001;
|
|
+ ESD_DEFAULT_RATE = 44100;
|
|
+ ESD_NAME_MAX = 128;
|
|
+ ESD_ENDIAN_KEY = (ord('E') shl 24) + (ord('N') shl 16) + (ord('D') shl 8) + (ord('N'));
|
|
+ ESD_MASK_BITS = $000F;
|
|
+ ESD_BITS8 = $0000;
|
|
+ ESD_BITS16 = $0001;
|
|
+ ESD_MASK_CHAN = $00F0;
|
|
+ ESD_MONO = $0010;
|
|
+ ESD_STEREO = $0020;
|
|
+ ESD_MASK_MODE = $0F00;
|
|
+ ESD_STREAM = $0000;
|
|
+ ESD_SAMPLE = $0100;
|
|
+ ESD_ADPCM = $0200;
|
|
+ ESD_MASK_FUNC = $F000;
|
|
+ ESD_PLAY = $1000;
|
|
+ ESD_MONITOR = $0000;
|
|
+ ESD_RECORD = $2000;
|
|
+ ESD_STOP = $0000;
|
|
+ ESD_LOOP = $2000;
|
|
+
|
|
+type
|
|
+ Tesd_proto = (ESD_PROTO_CONNECT,ESD_PROTO_LOCK,ESD_PROTO_UNLOCK,
|
|
+ ESD_PROTO_STREAM_PLAY,ESD_PROTO_STREAM_REC,
|
|
+ ESD_PROTO_STREAM_MON,ESD_PROTO_SAMPLE_CACHE,
|
|
+ ESD_PROTO_SAMPLE_FREE,ESD_PROTO_SAMPLE_PLAY,
|
|
+ ESD_PROTO_SAMPLE_LOOP,ESD_PROTO_SAMPLE_STOP,
|
|
+ ESD_PROTO_SAMPLE_KILL,ESD_PROTO_STANDBY,
|
|
+ ESD_PROTO_RESUME,ESD_PROTO_SAMPLE_GETID,
|
|
+ ESD_PROTO_STREAM_FILT,ESD_PROTO_SERVER_INFO,
|
|
+ ESD_PROTO_ALL_INFO,ESD_PROTO_SUBSCRIBE,
|
|
+ ESD_PROTO_UNSUBSCRIBE,ESD_PROTO_STREAM_PAN,
|
|
+ ESD_PROTO_SAMPLE_PAN,ESD_PROTO_STANDBY_MODE,
|
|
+ ESD_PROTO_LATENCY,ESD_PROTO_MAX);
|
|
+
|
|
+type
|
|
+ Pesd_format_t = ^Tesd_format_t;
|
|
+ Tesd_format_t = longint;
|
|
+
|
|
+ Pesd_proto_t = ^Tesd_proto_t;
|
|
+ Tesd_proto_t = longint;
|
|
+
|
|
+ Poctet = ^Toctet;
|
|
+ Toctet = byte;
|
|
+
|
|
+ TSoundFile = class(TObject)
|
|
+ private
|
|
+ FFileName: String;
|
|
+ FHandle: Longint;
|
|
+ FName: String;
|
|
+ procedure SetFileName(const AValue: String);
|
|
+ procedure SetHandle(const AValue: Longint);
|
|
+ public
|
|
+ property FileName: String read FFileName write SetFileName;
|
|
+ property Name: String read FName write FName;
|
|
+ property Handle: Longint read FHandle write SetHandle;
|
|
+ end;
|
|
+ PSoundFile = ^TSoundFile;
|
|
+
|
|
+ TESDSound = class(TComponent)
|
|
+ private
|
|
+ //functions from DLL's
|
|
+ esd_audio_open: function:Longint; cdecl;
|
|
+ esd_file_cache: function (esd:longint; name_prefix:Pchar; filename:Pchar):longint; cdecl;
|
|
+ esd_lock: function (esd:longint):longint; cdecl;
|
|
+ esd_unlock: function(esd:longint):longint; cdecl;
|
|
+ esd_audio_close: procedure; cdecl;
|
|
+ esd_sample_cache: function(esd:longint; format:Tesd_format_t; rate:longint; length:longint; name:Pchar):longint;cdecl;
|
|
+ esd_sample_play: function(esd:longint; sample:longint):longint;cdecl;
|
|
+ esd_open_sound: function(host:Pchar):longint;cdecl;
|
|
+ esd_sample_getid: function(esd:longint; name:Pchar):longint;cdecl;
|
|
+ esd_close: function(esd:longint):longint;cdecl;
|
|
+
|
|
+ esdlib: Pointer;
|
|
+ FEnabled: Boolean;
|
|
+ FHandle: Longint;
|
|
+ SoundFiles: PSoundFile; // dunno about this one
|
|
+ NumSoundFiles: Integer;
|
|
+ procedure SetEnabled(const AValue: Boolean);
|
|
+ procedure SetHandle(const AValue: Longint);
|
|
+ function initsound: Boolean;
|
|
+{$IFDEF UNIX}
|
|
+ function LoadLibrary(Aname: PChar): Pointer;
|
|
+ procedure CloseLibrary(AHandle: Pointer);
|
|
+ procedure LoadProcAddress(AHandle: Pointer; Aname: PChar; Address: Pointer);
|
|
+{$ENDIF}
|
|
+ public
|
|
+ Constructor Create(AOwner: TComponent); override;
|
|
+ Destructor Destroy; override;
|
|
+ procedure Play(ASample: String);
|
|
+ property Handle: Longint read FHandle write SetHandle;
|
|
+ function CacheSample(ASample, AName: String): Boolean;
|
|
+ published
|
|
+ Property Enabled: Boolean read FEnabled write SetEnabled;
|
|
+ end;
|
|
+
|
|
+procedure Register;
|
|
+
|
|
+implementation
|
|
+
|
|
+{$IFDEF UNIX}
|
|
+
|
|
+{LINKLIB m}
|
|
+
|
|
+function dlopen(AFile: PChar; mode: LongInt): Pointer; cdecl; external 'dl';
|
|
+function dlclose(handle: Pointer): LongInt; cdecl; external 'dl';
|
|
+function dlsym(handle: Pointer; name:PChar): Pointer; cdecl; external 'dl';
|
|
+
|
|
+{$ENDIF}
|
|
+
|
|
+{ TESDSound }
|
|
+
|
|
+{$IFDEF UNIX}
|
|
+
|
|
+function TESDSound.LoadLibrary(Aname: PChar): Pointer;
|
|
+begin
|
|
+ {$ifdef debug}
|
|
+ if ShowDebug > 0 then writeln('TESDSound::LoadLibrary');
|
|
+ {$endif}
|
|
+ try
|
|
+ Result := dlopen(Aname, 1);
|
|
+ except
|
|
+ end;
|
|
+end;
|
|
+
|
|
+procedure TESDSound.CloseLibrary(AHandle: Pointer);
|
|
+begin
|
|
+ {$ifdef debug}
|
|
+ if ShowDebug > 0 then writeln('TESDSound::CloseLibrary');
|
|
+ {$endif}
|
|
+
|
|
+ if assigned(AHandle) Then dlclose(AHandle);
|
|
+
|
|
+end;
|
|
+
|
|
+procedure TESDSound.LoadProcAddress(AHandle: Pointer; Aname: PChar;
|
|
+ Address: Pointer);
|
|
+begin
|
|
+ PPointer(Address)^ := dlsym(Ahandle, AName);
|
|
+end;
|
|
+
|
|
+{$ENDIF}
|
|
+
|
|
+function TESDSound.initsound: Boolean;
|
|
+begin
|
|
+{$IFDEF UNIX}
|
|
+ {$ifdef debug}
|
|
+ if ShowDebug > 0 then writeln('TESDSound::InitSound');
|
|
+ {$endif}
|
|
+ // load the sound libraries, if possible
|
|
+ try
|
|
+ esdlib := LoadLibrary('libesd.so.0');
|
|
+ if longint(esdlib) < 1 then esdlib := LoadLibrary('libesd.so'); // try this too
|
|
+
|
|
+ if longint(esdlib) > 0 then begin
|
|
+ LoadProcAddress(esdlib, 'esd_audio_open', @esd_audio_open);
|
|
+ LoadProcAddress(esdlib, 'esd_file_cache', @esd_file_cache);
|
|
+ LoadProcAddress(esdlib, 'esd_lock', @esd_lock);
|
|
+ LoadProcAddress(esdlib, 'esd_unlock', @esd_unlock);
|
|
+ LoadProcAddress(esdlib, 'esd_audio_close', @esd_audio_close);
|
|
+ LoadProcAddress(esdlib, 'esd_audio_open', @esd_audio_open);
|
|
+ LoadProcAddress(esdlib, 'esd_sample_cache', @esd_sample_cache);
|
|
+ LoadProcAddress(esdlib, 'esd_sample_play', @esd_sample_play);
|
|
+ LoadProcAddress(esdlib, 'esd_open_sound', @esd_open_sound);
|
|
+ LoadProcAddress(esdlib, 'esd_sample_getid', @esd_sample_getid);
|
|
+ LoadProcAddress(esdlib, 'esd_close', @esd_close);
|
|
+ Result := True;
|
|
+ end else result := False;
|
|
+ except
|
|
+ Result := False;
|
|
+ end;
|
|
+{$ENDIF}
|
|
+end;
|
|
+
|
|
+
|
|
+procedure TESDSound.SetEnabled(const AValue: Boolean);
|
|
+begin
|
|
+{$IFDEF UNIX}
|
|
+ {$ifdef debug}
|
|
+ if ShowDebug > 0 then writeln('TESDSound::SetEnabled');
|
|
+ {$endif}
|
|
+ if FEnabled=AValue then exit;
|
|
+ if AValue = false then begin
|
|
+ FEnabled := AValue;
|
|
+ if not(csDesigning in componentstate) then begin
|
|
+ if handle > 0 then begin
|
|
+ //esd_audio_close;
|
|
+ esd_close(handle);
|
|
+ end;
|
|
+ closelibrary(esdlib);
|
|
+ end; // if not designing
|
|
+ exit;
|
|
+ end;
|
|
+
|
|
+ if not(csDesigning in componentstate) then begin
|
|
+ InitSound;
|
|
+ handle := esd_open_sound(nil);
|
|
+ if handle > 0 then FEnabled := True;
|
|
+ FEnabled := True;
|
|
+ end else FEnabled := AValue;
|
|
+{$ENDIF}
|
|
+end;
|
|
+
|
|
+procedure TESDSound.SetHandle(const AValue: Longint);
|
|
+begin
|
|
+ if FHandle=AValue then exit;
|
|
+ FHandle:=AValue;
|
|
+end;
|
|
+
|
|
+constructor TESDSound.Create(AOwner: TComponent);
|
|
+begin
|
|
+ {$ifdef debug}
|
|
+ if ShowDebug > 0 then writeln('TESDSound::Create');
|
|
+ {$endif}
|
|
+
|
|
+ Reallocmem(SoundFiles, 0);
|
|
+ NumSoundFiles := 0;
|
|
+
|
|
+ inherited Create(AOwner);
|
|
+
|
|
+end;
|
|
+
|
|
+
|
|
+destructor TESDSound.Destroy;
|
|
+var
|
|
+ I: Integer;
|
|
+begin
|
|
+{$IFDEF UNIX}
|
|
+ {$ifdef debug}
|
|
+ if ShowDebug > 0 then writeln('TESDSound::Destroy');
|
|
+ {$endif}
|
|
+ if handle > 0 then begin
|
|
+ esd_close(handle);
|
|
+ end;
|
|
+ closelibrary(esdlib);
|
|
+ if NumSoundFiles > 0 then begin
|
|
+ for I := 0 to NumSoundFiles-1 do begin
|
|
SoundFiles[i].Free;
|
|
end;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
ReallocMem(SoundFiles, 0);
|
|
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TESDSound.Play(ASample: String);
|
|
var
|
|
I: Integer;
|
|
begin
|
|
{$IFDEF UNIX}
|
|
if not(FEnabled) then begin
|
|
// error
|
|
raise exception.create('TESDSound:Play sample while disabled');
|
|
exit;
|
|
end;
|
|
|
|
if NumSoundFiles > 0 then begin
|
|
for I := 0 to NumSoundFiles - 1 do begin
|
|
// first look at the name
|
|
if SoundFiles[I].Name = ASample then begin
|
|
// found it!
|
|
if SoundFiles[i].Handle > 0 then begin
|
|
esd_sample_play(handle, SoundFiles[i].Handle);
|
|
exit;
|
|
end;
|
|
end else
|
|
if SoundFiles[i].FileName = ASample then begin
|
|
// found it by filename!
|
|
if SOundFiles[i].Handle > 0 then begin
|
|
esd_sample_play(handle, SoundFiles[i].Handle);
|
|
exit;
|
|
end;
|
|
end; // if found by filename
|
|
end;
|
|
end;
|
|
|
|
// didn't find it so try caching it...
|
|
if CacheSample(ASample, ASample) then begin
|
|
// got it... try to play it...
|
|
if NumSoundFiles > 0 then begin
|
|
for I := 0 to NumSoundFiles - 1 do begin
|
|
// first look at the name
|
|
if SoundFiles[I].Name = ASample then begin
|
|
// found it!
|
|
if SoundFiles[i].Handle > 0 then begin
|
|
esd_sample_play(handle, SoundFiles[i].Handle);
|
|
exit;
|
|
end;
|
|
end else
|
|
if SoundFiles[i].FileName = ASample then begin
|
|
// found it by filename!
|
|
if SOundFiles[i].Handle > 0 then begin
|
|
esd_sample_play(handle, SoundFiles[i].Handle);
|
|
exit;
|
|
end; // if handle > 0
|
|
end; // if found by filename
|
|
end; // for I
|
|
end; // if numsoundfiles > 0
|
|
end;
|
|
// oh well, I give up... just leave
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function TESDSound.CacheSample(ASample, AName: String): Boolean;
|
|
var
|
|
I: Integer;
|
|
MySoundFile: TSoundFile;
|
|
begin
|
|
{$IFDEF UNIX}
|
|
{$ifdef debug}
|
|
if ShowDebug > 0 then writeln('TESDSound::CacheSample "'+ASample+'" as "'+AName+'"');
|
|
{$endif}
|
|
if not(FEnabled) then begin
|
|
// error
|
|
raise exception.create('TESDSound:Cache sample while disabled');
|
|
exit;
|
|
end;
|
|
// see if it's already cached...
|
|
if NumSoundFiles > 0 then begin
|
|
for I := 0 to NumSoundFiles - 1 do begin
|
|
if SoundFiles[I].Name = AName then begin
|
|
// see if it's changing the file to use
|
|
if SoundFiles[i].FileName = ASample then exit; // nope
|
|
// change the file...
|
|
// I don't know how to release the cache, so just cache the new one
|
|
SoundFiles[i].Handle := esd_File_Cache(handle,'',PChar(ASample));
|
|
if SoundFiles[i].Handle < 1 then Result := False else Result := True;
|
|
exit;
|
|
end; // if found the name
|
|
end; // loop I
|
|
end; // if possibly cached...
|
|
|
|
// not loaded, so cache it
|
|
NumSoundFiles := NumSoundFiles + 1;
|
|
ReallocMem(SoundFiles,sizeof(TSoundFile) * NumSoundFiles);
|
|
MySoundFile := TSoundFile.Create;
|
|
SoundFiles[NumSoundFiles-1] := MySoundFile;
|
|
with MySoundFile do begin
|
|
Name := AName;
|
|
FileName := ASample;
|
|
end;
|
|
MySoundFile.Handle := esd_File_Cache(handle,'', pchar(ASample));
|
|
if MySoundFile.Handle < 1 then Result := False else Result := True;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
{ TSoundFile }
|
|
|
|
procedure TSoundFile.SetFileName(const AValue: String);
|
|
begin
|
|
if FFileName=AValue then exit;
|
|
FFileName:=AValue;
|
|
end;
|
|
|
|
procedure TSoundFile.SetHandle(const AValue: Longint);
|
|
begin
|
|
if FHandle=AValue then exit;
|
|
FHandle:=AValue;
|
|
end;
|
|
|
|
procedure Register;
|
|
begin
|
|
RegisterComponents('GamePack',[TESDSound]);
|
|
end;
|
|
|
|
|
|
initialization
|
|
{$I esdsound.lrs}
|
|
end.
|
|
|