Added OpenALSourceId() method to TOpenALAudioChannel
authorMark Sibly <blitzmunter@gmail.com>
Thu, 15 Oct 2015 03:38:00 +0000 (16:38 +1300)
committerMark Sibly <blitzmunter@gmail.com>
Thu, 15 Oct 2015 03:38:00 +0000 (16:38 +1300)
mod/brl.mod/openalaudio.mod/openalaudio.bmx

index 7da3300accaf617a1c377fa66326868b441fa7e2..430a826e168311cf03bf8e20948d45194a942c1f 100644 (file)
-
-Strict
-
-Rem
-bbdoc: Audio/OpenAL audio 
-about:
-The OpenAL audio module provide OpenAL drivers for use with the #audio module.
-End Rem
-Module BRL.OpenALAudio
-
-ModuleInfo "Version: 1.05"
-ModuleInfo "Author: Mark Sibly"
-ModuleInfo "License: zlib/libpng"
-ModuleInfo "Copyright: Blitz Research Ltd"
-ModuleInfo "Modserver: BRL"
-
-ModuleInfo "History: 1.05 Release"
-ModuleInfo "History: Fixed default channel volume,pan,rate"
-ModuleInfo "History: Fixed behaviour of channel going out of scope"
-ModuleInfo "History: 1.04 Release"
-ModuleInfo "History: Fixed channel playing to return true if active (playing or paused)"
-ModuleInfo "History: 1.03 Release"
-ModuleInfo "History: Added (synced) alDeleteBuffers"
-ModuleInfo "History: 1.02 Release"
-ModuleInfo "History: Added EnableOpenALAudio"
-ModuleInfo "History: 1.01 Initial Release"
-
-Import BRL.Math
-Import BRL.Audio
-Import Pub.OpenAL
-
-Private
-
-Const CLOG=False
-
-'list of all non-static sources
-Global _sources:TOpenALSource
-
-Function CheckAL()
-       Local err$
-       Select alGetError()
-       Case AL_NO_ERROR
-               Return True
-       Case AL_INVALID_NAME
-               err="INVALID_NAME"
-       Case AL_INVALID_ENUM
-               err="INVALID_ENUM"
-       Case AL_INVALID_VALUE
-               err="INVALID_VALUE"
-       Case AL_INVALID_OPERATION
-               err="INVALID_OPERATION"
-       Case AL_OUT_OF_MEMORY
-               err="OUT_OF_MEMORY"
-       Default
-               err="?????"
-       End Select
-       If CLOG WriteStdout "OpenAL Error: "+err+"~n"
-       Return False
-End Function
-
-Type TOpenALSource
-
-       Field _succ:TOpenALSource,_id,_seq,_sound:TOpenALSound,_avail
-       
-       Method Playing()
-               Local st
-               alGetSourcei _id,AL_SOURCE_STATE,Varptr st
-               Return st=AL_PLAYING
-       End Method
-                       
-       Method Paused()
-               Local st
-               alGetSourcei _id,AL_SOURCE_STATE,Varptr st
-               Return st=AL_PAUSED Or st=AL_INITIAL
-       End Method
-       
-       Method Active() 
-               Local st
-               alGetSourcei _id,AL_SOURCE_STATE,Varptr st
-               Return st=AL_PLAYING Or st=AL_PAUSED Or st=AL_INITIAL
-       End Method
-       
-       Method LogState()
-               Local st
-               alGetSourcei _id,AL_SOURCE_STATE,Varptr st
-               Select st
-               Case AL_PAUSED WriteStdout "AL_PAUSED~n"
-               Case AL_INITIAL WriteStdout "AL_INITIAL~n"
-               Case AL_STOPPED WriteStdout "AL_STOPPED~n"
-               Case AL_PLAYING WriteStdout "AL_PLAYING~n"
-               Default WriteStdout "AL_DUNNO, st="+st+"~n"
-               End Select
-       End Method
-       
-End Type
-
-Function EnumOpenALDevices$[]()
-       Local p:Byte Ptr=alcGetString( 0,ALC_DEVICE_SPECIFIER )
-       If Not p Return
-       Local devs$[100],n
-       While p[0] And n<100
-               Local sz
-               Repeat
-                       sz:+1
-               Until Not p[sz]
-               devs[n]=String.FromBytes( p,sz )
-               n:+1
-               p:+sz+1
-       Wend
-       Return devs[..n]
-End Function
-
-Public
-
-Type TOpenALSound Extends TSound
-
-       Method Delete()
-               alDeleteBuffers 1,Varptr _buffer
-               CheckAL
-               If CLOG WriteStdout "Deleted OpenAL buffer "+_buffer+"~n"
-       End Method
-
-       Method Play:TOpenALChannel( alloced_channel:TChannel=Null )
-               Local t:TOpenALChannel=Cue( alloced_channel )
-               t.SetPaused False
-               Return t
-       End Method
-
-       Method Cue:TOpenALChannel( alloced_channel:TChannel=Null )
-               Local t:TOpenALChannel=TOpenALChannel( alloced_channel )
-               If t
-                       Assert t._static
-               Else
-                       t=TOpenALChannel.Create( False )
-               EndIf
-               t.Cue Self
-               Return t
-       End Method
-
-       Function Create:TOpenALSound( sample:TAudioSample,flags )
-               Local alfmt
-               Select sample.format
-               Case SF_MONO8
-                       alfmt=AL_FORMAT_MONO8
-               Case SF_MONO16LE
-                       alfmt=AL_FORMAT_MONO16
-?BigEndian
-                       sample=sample.Convert( SF_MONO16BE )
-?
-               Case SF_MONO16BE
-                       alfmt=AL_FORMAT_MONO16
-?LittleEndian
-                       sample=sample.Convert( SF_MONO16LE )
-?
-               Case SF_STEREO8
-                       alfmt=AL_FORMAT_STEREO8
-               Case SF_STEREO16LE
-                       alfmt=AL_FORMAT_STEREO16
-?BigEndian
-                       sample=sample.Convert( SF_STEREO16BE )
-?
-               Case SF_STEREO16BE
-                       alfmt=AL_FORMAT_STEREO16
-?LittleEndian
-                       sample=sample.Convert( SF_STEREO16LE )
-?
-               End Select
-               
-               Local buffer=-1
-               alGenBuffers 1,Varptr buffer
-               CheckAL
-               
-               If CLOG WriteStdout "Generated OpenAL buffer "+buffer+"~n"
-               
-               alBufferData buffer,alfmt,sample.samples,sample.length*BytesPerSample[sample.format],sample.hertz
-               CheckAL
-               
-               Local t:TOpenALSound=New TOpenALSound
-               t._buffer=buffer
-               If (flags & 1) t._loop=1
-               Return t
-       End Function
-
-       Field _buffer,_loop
-
-End Type
-
-Type TOpenALChannel Extends TChannel
-
-       Method Delete()
-               If _seq<>_source._seq Return
-               
-               If _static      'LEAKED!
-                       Stop    'Just in case...                
-                       Return
-               EndIf
-               
-               If Not _source.Playing()
-                       alSourceStop _source._id
-                       alSourcei _source._id,AL_BUFFER,0
-                       _source._sound=Null
-               EndIf
-
-       End Method
-
-       Method Stop()
-               If _seq<>_source._seq Return
-       
-               _source._seq:+1
-
-               alSourceStop _source._id
-               alSourcei _source._id,AL_BUFFER,0
-               _source._sound=Null
-               
-               If _static
-                       _source._avail=True
-                       _source._succ=_sources
-                       _sources=_source
-               EndIf
-       End Method
-       
-       Method SetPaused( paused )
-               If _seq<>_source._seq Return
-
-               If paused
-                       alSourcePause _source._id
-               Else
-                       If _source.Paused() alSourcePlay _source._id
-               EndIf
-       End Method
-       
-       Method SetVolume( volume# )
-               If _seq<>_source._seq Return
-
-               alSourcef _source._id,AL_GAIN,volume
-       End Method
-       
-       Method SetPan( pan# )
-               If _seq<>_source._seq Return
-
-               pan:*90
-               alSource3f _source._id,AL_POSITION,Sin(pan),0,-Cos(pan)
-       End Method
-       
-       Method SetDepth( depth# )
-               If _seq<>_source._seq Return
-       End Method
-       
-       Method SetRate( rate# )
-               If _seq<>_source._seq Return
-
-               alSourcef _source._id,AL_PITCH,rate
-       End Method
-       
-       Method Playing()
-               If _seq<>_source._seq Return
-
-               Return _source.Playing()
-       End Method
-
-       Method Cue( sound:TOpenALSound )        
-               If _seq<>_source._seq Return
-
-               _source._sound=sound
-               alSourceRewind _source._id
-               alSourcei _source._id,AL_LOOPING,sound._loop
-               alSourcei _source._id,AL_BUFFER,sound._buffer
-       End Method
-       
-       Function Create:TOpenALChannel( static )
-       
-               Local source:TOpenALSource=_sources,pred:TOpenALSource
-               While source
-                       If source._avail Or Not source.Active()
-                               source._seq:+1
-                               If pred pred._succ=source._succ Else _sources=source._succ
-                               If CLOG WriteStdout "Recycling OpenAL source "+source._id+"~n"
-                               If CLOG source.LogState
-                               alSourceRewind source._id       'return to FA_INITIAL state
-                               If CLOG source.LogState
-                               alSourcei source._id,AL_BUFFER,0
-                               source._sound=Null
-                               Exit
-                       EndIf
-                       pred=source
-                       source=source._succ
-               Wend
-               
-               If Not source
-                       source=New TOpenALSource
-
-                       alGenSources 1,Varptr source._id
-                       CheckAL
-
-                       If source._id
-                               If CLOG WriteStdout "Generated OpenAL source "+source._id+"~n"
-                       Else
-                               If CLOG WriteStdout "Failed to generate OpenAL source~n"
-                       EndIf
-               EndIf
-               
-               Local t:TOpenALChannel=New TOpenALChannel
-               t._source=source
-               t._seq=source._seq
-               t._static=static
-               If source._id
-                       alSourcei source._id,AL_SOURCE_RELATIVE,True
-                       alSourcef source._id,AL_GAIN,1
-                       alSourcef source._id,AL_PITCH,1
-                       alSource3f source._id,AL_POSITION,0,0,1
-                       If Not static
-                               source._avail=False
-                               source._succ=_sources
-                               _sources=source
-                       EndIf
-               Else
-                       t._seq:+1
-               EndIf
-               Return t
-       End Function
-       
-       Field _source:TOpenALSource,_seq,_static
-       
-End Type
-
-Type TOpenALAudioDriver Extends TAudioDriver
-
-       Method Name$()
-               Return _name
-       End Method
-       
-       Method Startup()
-               _device=0
-               If _devname
-                       _device=alcOpenDevice( _devname )
-               Else If OpenALInstalled()
-                       _device=alcOpenDevice( Null )
-                       If Not _device
-                               _device=alcOpenDevice( "Generic Hardware" )
-                               If Not _device
-                                       _device=alcOpenDevice( "Generic Software" )
-                               EndIf
-                       EndIf
-               EndIf
-               If _device
-                       _context=alcCreateContext( _device,Null )
-                       If _context
-                               alcMakeContextCurrent _context
-                               alDistanceModel AL_NONE
-                               Return True
-                       EndIf
-                       alcCloseDevice( _device )
-               EndIf
-       End Method
-       
-       Method Shutdown()
-               _sources=Null
-               alcDestroyContext _context
-               alcCloseDevice _device
-       End Method
-
-       Method CreateSound:TOpenALSound( sample:TAudioSample,flags )
-               Return TOpenALSound.Create( sample,flags )
-       End Method
-       
-       Method AllocChannel:TOpenALChannel()
-               Return TOpenALChannel.Create( True )
-       End Method
-       
-       Function Create:TOpenALAudioDriver( name$,devname$ )
-               Local t:TOpenALAudioDriver=New TOpenALAudioDriver
-               t._name=name
-               t._devname=devname
-               Return t
-       End Function
-       
-       Field _name$,_devname$,_device,_context
-
-End Type
-
-If OpenALInstalled() TOpenALAudioDriver.Create "OpenAL",""
-
-Rem
-bbdoc: Enable OpenAL Audio
-returns: True if successful
-about:
-After successfully executing this command, OpenAL audio drivers will be added
-to the array of drivers returned by #AudioDrivers.
-End Rem
-Function EnableOpenALAudio()
-       Global done,okay
-       If done Return okay
-       If OpenALInstalled() And alcGetString
-               For Local devname$=EachIn EnumOpenALDevices()
-                       TOpenALAudioDriver.Create( "OpenAL "+devname,devname )
-               Next
-               TOpenALAudioDriver.Create "OpenAL Default",String.FromCString( alcGetString( 0,ALC_DEFAULT_DEVICE_SPECIFIER ) )
-               okay=True
-       EndIf
-       done=True
-       Return okay
-End Function
+\r
+Strict\r
+\r
+Rem\r
+bbdoc: Audio/OpenAL audio \r
+about:\r
+The OpenAL audio module provide OpenAL drivers for use with the #audio module.\r
+End Rem\r
+Module BRL.OpenALAudio\r
+\r
+ModuleInfo "Version: 1.05"\r
+ModuleInfo "Author: Mark Sibly"\r
+ModuleInfo "License: zlib/libpng"\r
+ModuleInfo "Copyright: Blitz Research Ltd"\r
+ModuleInfo "Modserver: BRL"\r
+\r
+ModuleInfo "History: 1.05 Release"\r
+ModuleInfo "History: Fixed default channel volume,pan,rate"\r
+ModuleInfo "History: Fixed behaviour of channel going out of scope"\r
+ModuleInfo "History: 1.04 Release"\r
+ModuleInfo "History: Fixed channel playing to return true if active (playing or paused)"\r
+ModuleInfo "History: 1.03 Release"\r
+ModuleInfo "History: Added (synced) alDeleteBuffers"\r
+ModuleInfo "History: 1.02 Release"\r
+ModuleInfo "History: Added EnableOpenALAudio"\r
+ModuleInfo "History: 1.01 Initial Release"\r
+\r
+Import BRL.Math\r
+Import BRL.Audio\r
+Import Pub.OpenAL\r
+\r
+Private\r
+\r
+Const CLOG=False\r
+\r
+'list of all non-static sources\r
+Global _sources:TOpenALSource\r
+\r
+Function CheckAL()\r
+       Local err$\r
+       Select alGetError()\r
+       Case AL_NO_ERROR\r
+               Return True\r
+       Case AL_INVALID_NAME\r
+               err="INVALID_NAME"\r
+       Case AL_INVALID_ENUM\r
+               err="INVALID_ENUM"\r
+       Case AL_INVALID_VALUE\r
+               err="INVALID_VALUE"\r
+       Case AL_INVALID_OPERATION\r
+               err="INVALID_OPERATION"\r
+       Case AL_OUT_OF_MEMORY\r
+               err="OUT_OF_MEMORY"\r
+       Default\r
+               err="?????"\r
+       End Select\r
+       If CLOG WriteStdout "OpenAL Error: "+err+"~n"\r
+       Return False\r
+End Function\r
+\r
+Type TOpenALSource\r
+\r
+       Field _succ:TOpenALSource,_id,_seq,_sound:TOpenALSound,_avail\r
+       \r
+       Method Playing()\r
+               Local st\r
+               alGetSourcei _id,AL_SOURCE_STATE,Varptr st\r
+               Return st=AL_PLAYING\r
+       End Method\r
+                       \r
+       Method Paused()\r
+               Local st\r
+               alGetSourcei _id,AL_SOURCE_STATE,Varptr st\r
+               Return st=AL_PAUSED Or st=AL_INITIAL\r
+       End Method\r
+       \r
+       Method Active() \r
+               Local st\r
+               alGetSourcei _id,AL_SOURCE_STATE,Varptr st\r
+               Return st=AL_PLAYING Or st=AL_PAUSED Or st=AL_INITIAL\r
+       End Method\r
+       \r
+       Method LogState()\r
+               Local st\r
+               alGetSourcei _id,AL_SOURCE_STATE,Varptr st\r
+               Select st\r
+               Case AL_PAUSED WriteStdout "AL_PAUSED~n"\r
+               Case AL_INITIAL WriteStdout "AL_INITIAL~n"\r
+               Case AL_STOPPED WriteStdout "AL_STOPPED~n"\r
+               Case AL_PLAYING WriteStdout "AL_PLAYING~n"\r
+               Default WriteStdout "AL_DUNNO, st="+st+"~n"\r
+               End Select\r
+       End Method\r
+       \r
+End Type\r
+\r
+Function EnumOpenALDevices$[]()\r
+       Local p:Byte Ptr=alcGetString( 0,ALC_DEVICE_SPECIFIER )\r
+       If Not p Return\r
+       Local devs$[100],n\r
+       While p[0] And n<100\r
+               Local sz\r
+               Repeat\r
+                       sz:+1\r
+               Until Not p[sz]\r
+               devs[n]=String.FromBytes( p,sz )\r
+               n:+1\r
+               p:+sz+1\r
+       Wend\r
+       Return devs[..n]\r
+End Function\r
+\r
+Public\r
+\r
+Type TOpenALSound Extends TSound\r
+\r
+       Method Delete()\r
+               alDeleteBuffers 1,Varptr _buffer\r
+               CheckAL\r
+               If CLOG WriteStdout "Deleted OpenAL buffer "+_buffer+"~n"\r
+       End Method\r
+\r
+       Method Play:TOpenALChannel( alloced_channel:TChannel=Null )\r
+               Local t:TOpenALChannel=Cue( alloced_channel )\r
+               t.SetPaused False\r
+               Return t\r
+       End Method\r
+\r
+       Method Cue:TOpenALChannel( alloced_channel:TChannel=Null )\r
+               Local t:TOpenALChannel=TOpenALChannel( alloced_channel )\r
+               If t\r
+                       Assert t._static\r
+               Else\r
+                       t=TOpenALChannel.Create( False )\r
+               EndIf\r
+               t.Cue Self\r
+               Return t\r
+       End Method\r
+\r
+       Function Create:TOpenALSound( sample:TAudioSample,flags )\r
+               Local alfmt\r
+               Select sample.format\r
+               Case SF_MONO8\r
+                       alfmt=AL_FORMAT_MONO8\r
+               Case SF_MONO16LE\r
+                       alfmt=AL_FORMAT_MONO16\r
+?BigEndian\r
+                       sample=sample.Convert( SF_MONO16BE )\r
+?\r
+               Case SF_MONO16BE\r
+                       alfmt=AL_FORMAT_MONO16\r
+?LittleEndian\r
+                       sample=sample.Convert( SF_MONO16LE )\r
+?\r
+               Case SF_STEREO8\r
+                       alfmt=AL_FORMAT_STEREO8\r
+               Case SF_STEREO16LE\r
+                       alfmt=AL_FORMAT_STEREO16\r
+?BigEndian\r
+                       sample=sample.Convert( SF_STEREO16BE )\r
+?\r
+               Case SF_STEREO16BE\r
+                       alfmt=AL_FORMAT_STEREO16\r
+?LittleEndian\r
+                       sample=sample.Convert( SF_STEREO16LE )\r
+?\r
+               End Select\r
+               \r
+               Local buffer=-1\r
+               alGenBuffers 1,Varptr buffer\r
+               CheckAL\r
+               \r
+               If CLOG WriteStdout "Generated OpenAL buffer "+buffer+"~n"\r
+               \r
+               alBufferData buffer,alfmt,sample.samples,sample.length*BytesPerSample[sample.format],sample.hertz\r
+               CheckAL\r
+               \r
+               Local t:TOpenALSound=New TOpenALSound\r
+               t._buffer=buffer\r
+               If (flags & 1) t._loop=1\r
+               Return t\r
+       End Function\r
+\r
+       Field _buffer,_loop\r
+\r
+End Type\r
+\r
+Rem\r
+bbdoc: OpenAL audio channel Type\r
+End Rem\r
+Type TOpenALChannel Extends TChannel\r
+\r
+       Method Delete()\r
+               If _seq<>_source._seq Return\r
+               \r
+               If _static      'LEAKED!\r
+                       Stop    'Just in case...                \r
+                       Return\r
+               EndIf\r
+               \r
+               If Not _source.Playing()\r
+                       alSourceStop _source._id\r
+                       alSourcei _source._id,AL_BUFFER,0\r
+                       _source._sound=Null\r
+               EndIf\r
+\r
+       End Method\r
+\r
+       Method Stop()\r
+               If _seq<>_source._seq Return\r
+       \r
+               _source._seq:+1\r
+\r
+               alSourceStop _source._id\r
+               alSourcei _source._id,AL_BUFFER,0\r
+               _source._sound=Null\r
+               \r
+               If _static\r
+                       _source._avail=True\r
+                       _source._succ=_sources\r
+                       _sources=_source\r
+               EndIf\r
+       End Method\r
+       \r
+       Method SetPaused( paused )\r
+               If _seq<>_source._seq Return\r
+\r
+               If paused\r
+                       alSourcePause _source._id\r
+               Else\r
+                       If _source.Paused() alSourcePlay _source._id\r
+               EndIf\r
+       End Method\r
+       \r
+       Method SetVolume( volume# )\r
+               If _seq<>_source._seq Return\r
+\r
+               alSourcef _source._id,AL_GAIN,volume\r
+       End Method\r
+       \r
+       Method SetPan( pan# )\r
+               If _seq<>_source._seq Return\r
+\r
+               pan:*90\r
+               alSource3f _source._id,AL_POSITION,Sin(pan),0,-Cos(pan)\r
+       End Method\r
+       \r
+       Method SetDepth( depth# )\r
+               If _seq<>_source._seq Return\r
+       End Method\r
+       \r
+       Method SetRate( rate# )\r
+               If _seq<>_source._seq Return\r
+\r
+               alSourcef _source._id,AL_PITCH,rate\r
+       End Method\r
+       \r
+       Method Playing()\r
+               If _seq<>_source._seq Return\r
+\r
+               Return _source.Playing()\r
+       End Method\r
+\r
+       Method Cue( sound:TOpenALSound )        \r
+               If _seq<>_source._seq Return\r
+\r
+               _source._sound=sound\r
+               alSourceRewind _source._id\r
+               alSourcei _source._id,AL_LOOPING,sound._loop\r
+               alSourcei _source._id,AL_BUFFER,sound._buffer\r
+       End Method\r
+       \r
+       Rem\r
+       bbdoc: Get native openal source id\r
+       returns: The native openal source id for the channel, or 0 if the channel\r
+       has been stopped using #Stop.\r
+       End Rem\r
+       Method OpenALSourceId()\r
+               If _seq<>_source._seq Return 0\r
+               Return _source._id\r
+       End Method\r
+       \r
+       Function Create:TOpenALChannel( static )\r
+       \r
+               Local source:TOpenALSource=_sources,pred:TOpenALSource\r
+               While source\r
+                       If source._avail Or Not source.Active()\r
+                               source._seq:+1\r
+                               If pred pred._succ=source._succ Else _sources=source._succ\r
+                               If CLOG WriteStdout "Recycling OpenAL source "+source._id+"~n"\r
+                               If CLOG source.LogState\r
+                               alSourceRewind source._id       'return to FA_INITIAL state\r
+                               If CLOG source.LogState\r
+                               alSourcei source._id,AL_BUFFER,0\r
+                               source._sound=Null\r
+                               Exit\r
+                       EndIf\r
+                       pred=source\r
+                       source=source._succ\r
+               Wend\r
+               \r
+               If Not source\r
+                       source=New TOpenALSource\r
+\r
+                       alGenSources 1,Varptr source._id\r
+                       CheckAL\r
+\r
+                       If source._id\r
+                               If CLOG WriteStdout "Generated OpenAL source "+source._id+"~n"\r
+                       Else\r
+                               If CLOG WriteStdout "Failed to generate OpenAL source~n"\r
+                       EndIf\r
+               EndIf\r
+               \r
+               Local t:TOpenALChannel=New TOpenALChannel\r
+               t._source=source\r
+               t._seq=source._seq\r
+               t._static=static\r
+               If source._id\r
+                       alSourcei source._id,AL_SOURCE_RELATIVE,True\r
+                       alSourcef source._id,AL_GAIN,1\r
+                       alSourcef source._id,AL_PITCH,1\r
+                       alSource3f source._id,AL_POSITION,0,0,1\r
+                       If Not static\r
+                               source._avail=False\r
+                               source._succ=_sources\r
+                               _sources=source\r
+                       EndIf\r
+               Else\r
+                       t._seq:+1\r
+               EndIf\r
+               Return t\r
+       End Function\r
+       \r
+       Field _source:TOpenALSource,_seq,_static\r
+       \r
+End Type\r
+\r
+Type TOpenALAudioDriver Extends TAudioDriver\r
+\r
+       Method Name$()\r
+               Return _name\r
+       End Method\r
+       \r
+       Method Startup()\r
+               _device=0\r
+               If _devname\r
+                       _device=alcOpenDevice( _devname )\r
+               Else If OpenALInstalled()\r
+                       _device=alcOpenDevice( Null )\r
+                       If Not _device\r
+                               _device=alcOpenDevice( "Generic Hardware" )\r
+                               If Not _device\r
+                                       _device=alcOpenDevice( "Generic Software" )\r
+                               EndIf\r
+                       EndIf\r
+               EndIf\r
+               If _device\r
+                       _context=alcCreateContext( _device,Null )\r
+                       If _context\r
+                               alcMakeContextCurrent _context\r
+                               alDistanceModel AL_NONE\r
+                               Return True\r
+                       EndIf\r
+                       alcCloseDevice( _device )\r
+               EndIf\r
+       End Method\r
+       \r
+       Method Shutdown()\r
+               _sources=Null\r
+               alcDestroyContext _context\r
+               alcCloseDevice _device\r
+       End Method\r
+\r
+       Method CreateSound:TOpenALSound( sample:TAudioSample,flags )\r
+               Return TOpenALSound.Create( sample,flags )\r
+       End Method\r
+       \r
+       Method AllocChannel:TOpenALChannel()\r
+               Return TOpenALChannel.Create( True )\r
+       End Method\r
+       \r
+       Function Create:TOpenALAudioDriver( name$,devname$ )\r
+               Local t:TOpenALAudioDriver=New TOpenALAudioDriver\r
+               t._name=name\r
+               t._devname=devname\r
+               Return t\r
+       End Function\r
+       \r
+       Field _name$,_devname$,_device,_context\r
+\r
+End Type\r
+\r
+If OpenALInstalled() TOpenALAudioDriver.Create "OpenAL",""\r
+\r
+Rem\r
+bbdoc: Enable OpenAL Audio\r
+returns: True if successful\r
+about:\r
+After successfully executing this command, OpenAL audio drivers will be added\r
+to the array of drivers returned by #AudioDrivers.\r
+End Rem\r
+Function EnableOpenALAudio()\r
+       Global done,okay\r
+       If done Return okay\r
+       If OpenALInstalled() And alcGetString\r
+               For Local devname$=EachIn EnumOpenALDevices()\r
+                       TOpenALAudioDriver.Create( "OpenAL "+devname,devname )\r
+               Next\r
+               TOpenALAudioDriver.Create "OpenAL Default",String.FromCString( alcGetString( 0,ALC_DEFAULT_DEVICE_SPECIFIER ) )\r
+               okay=True\r
+       EndIf\r
+       done=True\r
+       Return okay\r
+End Function\r