2011-11-14 20:26:08 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
unit fpsound_openal;
|
|
|
|
|
|
|
|
{$mode objfpc}
|
|
|
|
|
|
|
|
interface
|
|
|
|
|
|
|
|
uses
|
2012-01-25 14:30:25 +00:00
|
|
|
Classes, SysUtils, openal, fpsound;
|
2011-11-14 20:26:08 +00:00
|
|
|
|
|
|
|
// openal
|
|
|
|
const
|
|
|
|
// Note: if you lower the al_bufcount, then you have to modify the al_polltime also!
|
|
|
|
al_bufcount = 4;
|
|
|
|
al_polltime = 100;
|
|
|
|
|
|
|
|
var
|
|
|
|
al_device : PALCdevice;
|
|
|
|
al_context : PALCcontext;
|
|
|
|
|
|
|
|
type
|
2012-01-23 10:43:32 +00:00
|
|
|
|
|
|
|
{ TOpenALPlayer }
|
|
|
|
|
2011-11-14 21:00:46 +00:00
|
|
|
TOpenALPlayer = class(TSoundPlayer)
|
2011-11-14 20:26:08 +00:00
|
|
|
private
|
|
|
|
source : TStream;
|
|
|
|
codec_bs : Longword;
|
|
|
|
OPCSoundWasInitialized: Boolean;
|
|
|
|
OPCSoundStreamIsLoaded: Boolean;
|
|
|
|
al_source : ALuint;
|
|
|
|
al_format : Integer;
|
|
|
|
al_buffers : array[0..al_bufcount-1] of ALuint;
|
|
|
|
al_bufsize : Longword;
|
|
|
|
al_readbuf : Pointer;
|
|
|
|
al_rate : Longword;
|
2011-11-14 21:00:46 +00:00
|
|
|
public
|
2012-01-23 10:43:32 +00:00
|
|
|
procedure Initialize; override;
|
|
|
|
procedure Finalize; override;
|
|
|
|
procedure Play(ASound: TSoundDocument); override;
|
2012-01-25 08:44:37 +00:00
|
|
|
procedure AdjustToKeyElement(ASound: TSoundDocument; AKeyElement: TSoundKeyElement);
|
2011-11-14 20:26:08 +00:00
|
|
|
procedure alStop;
|
2012-01-25 08:44:37 +00:00
|
|
|
function alProcess(ASound: TSoundDocument; AKeyElement: TSoundKeyElement): Boolean;
|
|
|
|
function alFillBuffer(ASound: TSoundDocument; AKeyElement: TSoundKeyElement): Integer;
|
2011-11-14 20:26:08 +00:00
|
|
|
end;
|
|
|
|
|
2011-11-14 21:00:46 +00:00
|
|
|
|
2011-11-14 20:26:08 +00:00
|
|
|
implementation
|
|
|
|
|
2011-11-14 21:00:46 +00:00
|
|
|
procedure TOpenALPlayer.alStop;
|
2011-11-14 20:26:08 +00:00
|
|
|
begin
|
|
|
|
alSourceStop(al_source);
|
|
|
|
alSourceRewind(al_source);
|
|
|
|
alSourcei(al_source, AL_BUFFER, 0);
|
|
|
|
end;
|
|
|
|
|
2012-01-25 08:44:37 +00:00
|
|
|
function TOpenALPlayer.alProcess(ASound: TSoundDocument; AKeyElement: TSoundKeyElement): Boolean;
|
2011-11-14 20:26:08 +00:00
|
|
|
var
|
|
|
|
processed : ALint;
|
|
|
|
buffer : ALuint;
|
|
|
|
sz : Integer;
|
|
|
|
begin
|
|
|
|
alGetSourcei(al_source, AL_BUFFERS_PROCESSED, processed);
|
|
|
|
while (processed > 0) and (processed <= al_bufcount) do
|
|
|
|
begin
|
|
|
|
alSourceUnqueueBuffers(al_source, 1, @buffer);
|
2012-01-25 08:44:37 +00:00
|
|
|
sz := alFillBuffer(ASound, AKeyELement);
|
2011-11-14 20:26:08 +00:00
|
|
|
if sz <= 0 then
|
|
|
|
begin
|
|
|
|
Exit(False);
|
|
|
|
end;
|
|
|
|
alBufferData(buffer, al_format, al_readbuf, sz, al_rate);
|
|
|
|
alSourceQueueBuffers(al_source, 1, @buffer);
|
|
|
|
Dec(processed);
|
|
|
|
end;
|
|
|
|
Result := True;
|
|
|
|
end;
|
|
|
|
|
2012-01-25 08:44:37 +00:00
|
|
|
function TOpenALPlayer.alFillBuffer(ASound: TSoundDocument; AKeyElement: TSoundKeyElement): Integer;
|
2012-01-23 10:43:32 +00:00
|
|
|
var
|
2012-01-25 08:44:37 +00:00
|
|
|
lCurElement: TSoundElement;
|
2012-01-23 10:43:32 +00:00
|
|
|
lReadCount: Integer = 0;
|
|
|
|
begin
|
2012-01-25 08:44:37 +00:00
|
|
|
Result := 0;
|
2012-01-23 10:43:32 +00:00
|
|
|
while lReadCount < al_bufsize do
|
|
|
|
begin
|
2012-01-25 08:44:37 +00:00
|
|
|
lCurElement := ASound.GetNextSoundElement();
|
|
|
|
if lCurElement = nil then Exit;
|
2012-01-23 10:43:32 +00:00
|
|
|
|
2012-01-25 08:44:37 +00:00
|
|
|
Inc(Result);
|
2012-01-23 10:43:32 +00:00
|
|
|
lReadCount := lReadCount + AKeyElement.BitsPerSample div 8;
|
|
|
|
|
|
|
|
if AKeyElement.BitsPerSample = 8 then
|
2012-01-25 08:44:37 +00:00
|
|
|
PByte(al_readbuf)[lReadCount] := Lo((lCurElement as TSoundSample8).ChannelValues[0])
|
2012-01-23 10:43:32 +00:00
|
|
|
else
|
2012-01-25 08:44:37 +00:00
|
|
|
PWord(al_readbuf)[lReadCount div 2] := Word((lCurElement as TSoundSample16).ChannelValues[0])
|
2012-01-23 10:43:32 +00:00
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TOpenALPlayer.Initialize;
|
|
|
|
begin
|
2012-01-25 14:28:29 +00:00
|
|
|
// alutInit(0, NULL);
|
|
|
|
|
|
|
|
{ alListenerfv(AL_POSITION,listenerPos);
|
|
|
|
alListenerfv(AL_VELOCITY,listenerVel);
|
|
|
|
alListenerfv(AL_ORIENTATION,listenerOri);}
|
|
|
|
|
2012-01-23 10:43:32 +00:00
|
|
|
alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED);
|
|
|
|
alGenSources(1, @al_source);
|
|
|
|
alGenBuffers(al_bufcount, @al_buffers);
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TOpenALPlayer.Finalize;
|
|
|
|
begin
|
|
|
|
// finalize openal
|
|
|
|
alDeleteSources(1, @al_source);
|
|
|
|
alDeleteBuffers(al_bufcount, @al_buffers);
|
2012-01-25 14:28:29 +00:00
|
|
|
if al_readbuf <> nil then FreeMem(al_readbuf);
|
2012-01-23 10:43:32 +00:00
|
|
|
|
|
|
|
// wave.fStream := nil;
|
|
|
|
// source := nil;
|
|
|
|
|
|
|
|
// finalize codec
|
2012-01-25 14:28:29 +00:00
|
|
|
// wave.Free;
|
2012-01-23 10:43:32 +00:00
|
|
|
|
|
|
|
// close file
|
|
|
|
// source.Free;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TOpenALPlayer.Play(ASound: TSoundDocument);
|
2011-11-14 20:26:08 +00:00
|
|
|
var
|
|
|
|
i: Integer;
|
|
|
|
queued : Integer;
|
|
|
|
done : Boolean;
|
2012-01-23 10:43:32 +00:00
|
|
|
lKeyElement: TSoundKeyElement;
|
2012-01-25 14:28:29 +00:00
|
|
|
//
|
|
|
|
{ buffer : Cardinal;
|
|
|
|
sourcepos: array [0..2] of Single=(0.0, 0.0, 0.0);
|
|
|
|
sourcevel: array [0..2] of Single=(0.0, 0.0, 0.0);
|
|
|
|
listenerpos: array [0..2] of Single=(0.0, 0.0, 0.0);
|
|
|
|
listenervel: array [0..2] of Single=(0.0, 0.0, 0.0);
|
|
|
|
listenerori: array [0..5] of Single=(0.0, 0.0, -1.0, 0.0, 1.0, 0.0);
|
|
|
|
Context: PALCcontext;
|
|
|
|
Device: PALCdevice; }
|
2011-11-14 20:26:08 +00:00
|
|
|
begin
|
2012-01-25 14:28:29 +00:00
|
|
|
Initialize();
|
|
|
|
|
2012-01-23 10:43:32 +00:00
|
|
|
// First adjust to the first key element
|
|
|
|
lKeyElement := ASound.GetFirstSoundElement();
|
2012-01-25 08:44:37 +00:00
|
|
|
AdjustToKeyElement(ASound, lKeyElement);
|
2011-11-14 20:26:08 +00:00
|
|
|
|
2012-01-23 10:43:32 +00:00
|
|
|
// Now clean up the source
|
2011-11-14 20:26:08 +00:00
|
|
|
alSourceStop(al_source);
|
|
|
|
alSourceRewind(al_source);
|
|
|
|
alSourcei(al_source, AL_BUFFER, 0);
|
|
|
|
|
|
|
|
for i := 0 to al_bufcount - 1 do
|
|
|
|
begin
|
2012-01-23 10:43:32 +00:00
|
|
|
// Fill the buffer
|
|
|
|
AlFillBuffer(ASound, lKeyElement);
|
2011-11-14 20:26:08 +00:00
|
|
|
|
|
|
|
alBufferData(al_buffers[i], al_format, al_readbuf, al_bufsize, al_rate);
|
|
|
|
alSourceQueueBuffers(al_source, 1, @al_buffers[i]);
|
|
|
|
end;
|
|
|
|
|
|
|
|
// Under windows, AL_LOOPING = AL_TRUE breaks queueing, no idea why
|
|
|
|
alSourcei(al_source, AL_LOOPING, AL_FALSE);
|
|
|
|
alSourcePlay(al_source);
|
|
|
|
|
|
|
|
done:=False;
|
|
|
|
queued:=0;
|
|
|
|
repeat
|
2012-01-25 08:44:37 +00:00
|
|
|
if alProcess(ASound, lKeyElement) then
|
|
|
|
begin
|
2011-11-14 20:26:08 +00:00
|
|
|
alGetSourcei(al_source, AL_BUFFERS_QUEUED, queued);
|
|
|
|
done:=queued=0;
|
|
|
|
end;
|
|
|
|
Sleep(al_polltime);
|
|
|
|
until done;
|
2012-01-25 14:28:29 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
AlSourceStop(source);
|
|
|
|
AlGenBuffers(1, @buffer);
|
|
|
|
loop:=0;
|
|
|
|
LoadWavStream(AStream, format, data, size, freq, loop);
|
|
|
|
AlBufferData(buffer, format, data, size, freq);
|
|
|
|
|
|
|
|
if data<>nil then freemem(data);
|
|
|
|
|
|
|
|
AlGenSources(1, @source);
|
|
|
|
AlSourcei(source, AL_BUFFER, buffer);
|
|
|
|
AlSourcef(source, AL_PITCH, 1.0);
|
|
|
|
AlSourcef(source, AL_GAIN, 1.0);
|
|
|
|
AlSourcefv(source, AL_POSITION, @sourcepos);
|
|
|
|
AlSourcefv(source, AL_VELOCITY, @sourcevel);
|
|
|
|
// Under windows, AL_LOOPING = AL_TRUE breaks queueing, no idea why
|
|
|
|
// AlSourcei(source, AL_LOOPING, AL_TRUE);
|
|
|
|
|
|
|
|
AlListenerfv(AL_POSITION, @listenerpos);
|
|
|
|
AlListenerfv(AL_VELOCITY, @listenervel);
|
|
|
|
AlListenerfv(AL_ORIENTATION, @listenerori);
|
|
|
|
AlSourcePlay(source);}
|
2011-11-14 20:26:08 +00:00
|
|
|
end;
|
|
|
|
|
2012-01-25 08:44:37 +00:00
|
|
|
procedure TOpenALPlayer.AdjustToKeyElement(ASound: TSoundDocument; AKeyElement: TSoundKeyElement);
|
2011-11-14 20:26:08 +00:00
|
|
|
begin
|
|
|
|
// define codec
|
2012-01-23 10:43:32 +00:00
|
|
|
//source := AStream;
|
2011-11-14 20:26:08 +00:00
|
|
|
|
|
|
|
// inittialize codec
|
2012-01-25 08:44:37 +00:00
|
|
|
if AKeyElement.Channels = 1 then
|
2012-01-23 10:43:32 +00:00
|
|
|
begin
|
2012-01-25 08:44:37 +00:00
|
|
|
if AKeyElement.BitsPerSample=8 then al_format:=AL_FORMAT_MONO8
|
2011-11-14 20:26:08 +00:00
|
|
|
else al_format:=AL_FORMAT_MONO16
|
2012-01-23 10:43:32 +00:00
|
|
|
end
|
|
|
|
else
|
|
|
|
begin
|
2012-01-25 08:44:37 +00:00
|
|
|
if AKeyElement.BitsPerSample=8 then al_format := AL_FORMAT_STEREO8
|
2012-01-23 10:43:32 +00:00
|
|
|
else al_format:=AL_FORMAT_STEREO16
|
2011-11-14 20:26:08 +00:00
|
|
|
end;
|
|
|
|
|
2012-01-25 08:44:37 +00:00
|
|
|
codec_bs:=2*AKeyElement.Channels;
|
2011-11-14 20:26:08 +00:00
|
|
|
al_bufsize := 20000 - (20000 mod codec_bs);
|
2012-01-25 08:44:37 +00:00
|
|
|
al_rate:=AKeyElement.SampleRate;
|
2011-11-14 20:26:08 +00:00
|
|
|
// WriteLn('Blocksize : ', codec_bs);
|
|
|
|
// WriteLn('Rate : ', wave.fmt.SampleRate);
|
|
|
|
// WriteLn('Channels : ', wave.fmt.Channels);
|
|
|
|
// WriteLn('OpenAL Buffers : ', al_bufcount);
|
|
|
|
// WriteLn('OpenAL Buffer Size : ', al_bufsize);
|
|
|
|
|
2012-01-25 14:28:29 +00:00
|
|
|
if al_readbuf <> nil then FreeMem(al_readbuf);
|
|
|
|
GetMem(al_readbuf, al_bufsize);
|
|
|
|
|
2012-01-25 08:44:37 +00:00
|
|
|
alProcess(ASound, AKeyElement);
|
2011-11-14 20:26:08 +00:00
|
|
|
end;
|
|
|
|
|
2012-01-25 14:28:29 +00:00
|
|
|
initialization
|
|
|
|
RegisterSoundPlayer(TOpenALPlayer.Create, spOpenAL);
|
2011-11-14 20:26:08 +00:00
|
|
|
end.
|
|
|
|
|