lazarus-ccr/applications/tappytux/edssound.pas
2011-08-25 09:29:40 +00:00

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.