MODULE OpenALSound;
IMPORT S := SYSTEM, Modules, SoundDevices, AL := OpenAL, Objects, Files,
Strings, Streams, Log := KernelLog;
CONST
StatePlaying = 1;
StatePaused = 3;
StateStoped = 4;
StateClosed = 5;
Debug = TRUE;
NumBuffers = 20;
OpenALConfig ="openalplay.ini";
TYPE
Driver = OBJECT(SoundDevices.Driver)
VAR
enabled: BOOLEAN;
playChannel: PlayChannel;
ctx: AL.ALCcontext;
dev: AL.ALCdevice;
PROCEDURE & Initialize;
BEGIN
SetName("OpenAL sound driver");
desc := "Sound driver for UnixAos";
enabled := FALSE;
playChannel := NIL
END Initialize;
PROCEDURE CreateALContext( ): BOOLEAN;
VAR name: ARRAY 64 OF CHAR; ok: BOOLEAN;
BEGIN
GetDeviceName( name );
dev := AL.alcOpenDevice( name );
IF dev # 0 THEN
ctx := AL.alcCreateContext( dev, 0 );
ok := AL.alcMakeContextCurrent( ctx );
IF ok THEN RETURN TRUE END
END;
Log.Enter; Log.String( "OpenALSound.Driver.CreateALContext: failed!" ); Log.Exit;
RETURN FALSE
END CreateALContext;
PROCEDURE DestroyALContext;
VAR ignore: BOOLEAN;
BEGIN
ignore := AL.alcMakeContextCurrent( 0 );
AL.alcDestroyContext( ctx );
ignore := AL.alcCloseDevice( dev );
END DestroyALContext;
PROCEDURE Enable*;
BEGIN
enabled := TRUE;
IF playChannel # NIL THEN playChannel.Start END;
END Enable;
PROCEDURE Disable*;
BEGIN
IF playChannel # NIL THEN playChannel.Pause END;
enabled := FALSE
END Disable;
PROCEDURE GetNativeFrequency*(nr : LONGINT) : LONGINT;
BEGIN
RETURN 48000;
END GetNativeFrequency;
PROCEDURE OpenPlayChannel*( VAR channel: SoundDevices.Channel;
samplingRate, samplingResolution: LONGINT;
nofSubChannels, format : LONGINT;
VAR res : LONGINT );
BEGIN {EXCLUSIVE}
IF ~enabled THEN
Log.Enter;
Log.String( "OpenALSound.OpenPlayChannel: OpenAL driver is disabled" );
Log.Exit;
res := SoundDevices.ResNoMoreChannels;
RETURN
END;
IF playChannel # NIL THEN
Log.Enter;
Log.String( "OpenALSound.OpenPlayChannel: OpenAL driver already in use" );
Log.Exit;
res := SoundDevices.ResNoMoreChannels;
RETURN
END;
channel := NIL;
IF ~((samplingRate > 0) & (samplingRate <= 48000)) THEN
res := SoundDevices.ResUnsupportedFrequency;
RETURN
END;
IF ~(samplingResolution IN {8, 16}) THEN
res := SoundDevices.ResUnsupportedSamplingRes;
RETURN
END;
IF (nofSubChannels # 1) & (nofSubChannels # 2) THEN
res := SoundDevices.ResUnsupportedSubChannels;
RETURN
END;
IF format # SoundDevices.FormatPCM THEN
res := SoundDevices.ResUnsupportedFormat;
RETURN
END;
IF ~CreateALContext( ) THEN
res := SoundDevices.ResNoMoreChannels;
RETURN
END;
NEW( playChannel, SELF, samplingRate, samplingResolution, nofSubChannels );
channel := playChannel;
IF Debug THEN
Log.Enter;
Log.String("OpenALSound.OpenPlayChannel: ");
Log.Int(samplingRate, 1); Log.String("Hz, ");
Log.Int(samplingResolution, 1); Log.String("bit, ");
Log.Int(nofSubChannels, 1); Log.String(" channel(s)");
Log.Exit
END;
res := SoundDevices.ResOK
END OpenPlayChannel;
PROCEDURE OpenRecordChannel*( VAR channel: SoundDevices.Channel;
samplingRate, samplingResolution: LONGINT;
nofSubChannels, format : LONGINT;
VAR res : LONGINT );
BEGIN {EXCLUSIVE}
res := SoundDevices.ResNoMoreChannels;
channel := NIL
END OpenRecordChannel;
PROCEDURE RegisterMixerChangeListener*(mixChangedProc : SoundDevices.MixerChangedProc);
BEGIN {EXCLUSIVE}
END RegisterMixerChangeListener;
PROCEDURE UnregisterMixerChangeListener*(mixChangeProc : SoundDevices.MixerChangedProc);
BEGIN {EXCLUSIVE}
END UnregisterMixerChangeListener;
PROCEDURE GetMixerChannel*(channelNr : LONGINT; VAR channel : SoundDevices.MixerChannel);
BEGIN
channel := NIL
END GetMixerChannel;
PROCEDURE GetNofMixerChannels*() : LONGINT;
BEGIN
RETURN 0
END GetNofMixerChannels;
END Driver;
PlayChannel = OBJECT( SoundDevices.Channel );
VAR
source: AL.ALuint;
buffers: ARRAY NumBuffers OF AL.ALuint;
freeBuffers: AL.ALint;
bufferListener: SoundDevices.BufferListener;
frequency: AL.ALuint;
format: AL.ALenum;
count: LONGINT;
volume: LONGINT;
state: LONGINT;
driver: Driver;
PROCEDURE &Initialize*( driver: Driver; samplingRate, bitsPerSample, nofChannels : LONGINT );
BEGIN
SELF.driver := driver;
AL.alGenBuffers( NumBuffers, S.ADR( buffers[0] ) );
AL.alGenSources( 1, S.ADR( source ) );
IF AL.alGetError() # AL.AL_NO_ERROR THEN
Log.Enter;
Log.String( "PlayChannel.Initialize: failed to allocat sources" );
Log.Exit
END;
frequency := samplingRate;
IF nofChannels = 1 THEN
CASE bitsPerSample OF
| 8: format := AL.AL_FORMAT_MONO8
|16: format := AL.AL_FORMAT_MONO16
ELSE
format:= AL.AL_FORMAT_MONO8;
END;
ELSIF nofChannels = 2 THEN
CASE bitsPerSample OF
| 8: format := AL.AL_FORMAT_STEREO8
|16: format := AL.AL_FORMAT_STEREO16
ELSE
format:= AL.AL_FORMAT_STEREO8;
END;
ELSE
format := AL.AL_FORMAT_MONO8
END;
count := 0; freeBuffers := 0;
state := StateStoped
END Initialize;
PROCEDURE RegisterBufferListener*( bufferListener : SoundDevices.BufferListener );
BEGIN
SELF.bufferListener := bufferListener
END RegisterBufferListener;
PROCEDURE QueueBuffer*( x : SoundDevices.Buffer );
VAR buffer: AL.ALuint; alstate: AL.ALint;
BEGIN {EXCLUSIVE}
AWAIT( state = StatePlaying );
IF count < NumBuffers THEN
AL.alBufferData( buffers[count], format, S.ADR( x.data[0] ), x.len, frequency );
bufferListener( x );
INC( count );
IF count = NumBuffers THEN
AL.alSourceQueueBuffers( source, count, S.ADR( buffers[0] ) );
AL.alSourcePlay( source );
IF AL.alGetError() # AL.AL_NO_ERROR THEN
Log.Enter;
Log.String( "OpenALSound.PlayChannel.QueueBuffer: start paying failed" );
Log.Exit
END
END
ELSE
REPEAT
AL.alGetSourcei( source, AL.AL_BUFFERS_PROCESSED, freeBuffers );
IF freeBuffers <= 0 THEN Objects.Sleep( 5 ) END;
UNTIL freeBuffers > 0;
AL.alSourceUnqueueBuffers( source, 1, S.ADR( buffer ) );
DEC( freeBuffers );
AL.alBufferData( buffer, format, S.ADR( x.data[0] ), x.len, frequency );
bufferListener( x );
INC( count );
AL.alSourceQueueBuffers( source, 1, S.ADR( buffer ) );
IF AL.alGetError( ) # AL.AL_NO_ERROR THEN
Log.Enter;
Log.String( "OpenALSound.PlayChannel.QueueBuffer: queueing failed" ); Log.Int( count, 5 );
Log.Exit;
RETURN
END;
AL.alGetSourcei( source, AL.AL_SOURCE_STATE, alstate );
IF alstate # AL.AL_PLAYING THEN
AL.alSourcePlay( source )
END;
END
END QueueBuffer;
PROCEDURE SetVolume*( vol : LONGINT );
VAR gain: AL.ALfloat;
BEGIN
IF vol < 0 THEN vol := 0
ELSIF vol > 255 THEN vol := 255
END;
volume := vol;
gain := volume/255;
AL.alSourcef(source, AL.AL_GAIN, gain);
END SetVolume;
PROCEDURE GetVolume*() : LONGINT;
BEGIN
RETURN SELF.volume
END GetVolume;
PROCEDURE GetPosition*() : LONGINT;
BEGIN
RETURN 0
END GetPosition;
PROCEDURE Start*;
BEGIN {EXCLUSIVE}
ASSERT(state # StateClosed);
state := StatePlaying
END Start;
PROCEDURE Pause*;
BEGIN
state := StatePaused
END Pause;
PROCEDURE Stop*;
BEGIN
state := StateStoped;
END Stop;
PROCEDURE Close*;
BEGIN
Stop;
AL.alDeleteSources( 1, source );
AL.alDeleteBuffers( NumBuffers, S.ADR( buffers[0] ) );
driver.DestroyALContext;
driver.playChannel := NIL;
state := StateClosed;
IF Debug THEN
Log.Enter; Log.String( "OpenALSound: play cannel closed" ); Log.Exit
END
END Close;
END PlayChannel;
VAR driver: Driver;
PROCEDURE GetDeviceName( VAR sdev: ARRAY OF CHAR );
VAR file: Files.File;
rd: Files.Reader;
found: BOOLEAN;
BEGIN
sdev := "";
file := Files.Old( OpenALConfig );
IF file = NIL THEN RETURN END;
Files.OpenReader( rd, file, 0 );
rd.SkipWhitespace( );
found := FALSE ;
WHILE (~found) & (rd.res = Streams.Ok) DO
rd.Ln( sdev );
Strings.Trim(sdev, " ");
found := sdev[0] # "#";
rd.SkipWhitespace( );
END;
END GetDeviceName;
PROCEDURE Install*;
VAR res: LONGINT;
BEGIN {EXCLUSIVE}
IF driver = NIL THEN
NEW( driver );
SoundDevices.devices.Add( driver, res );
IF res = 0 THEN
driver.Enable;
Log.Enter; Log.String( "OpenAL sound driver installed" ); Log.Exit
END
END;
END Install;
PROCEDURE Enable*;
BEGIN
IF driver # NIL THEN driver.Enable END
END Enable;
PROCEDURE Disable*;
BEGIN
IF driver # NIL THEN driver.Disable END
END Disable;
PROCEDURE Cleanup;
BEGIN
Disable;
IF driver # NIL THEN
SoundDevices.devices.Remove( driver );
driver := NIL;
Log.Enter; Log.String( "OpenAL sound driver removed" ); Log.Exit
END
END Cleanup;
BEGIN
Modules.InstallTermHandler( Cleanup )
END OpenALSound.
SystemTools.Free MP3Player OpenALSound ~
OpenALSound.Install ~
OpenALSound.Disable ~
OpenALSound.Enable ~
MP3Player.Open test.mp3 ~
MP3Player.Open sesta.mp3 ~