123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402 |
- SuperStrict
- 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:Int=False
- 'list of all non-static sources
- Global _sources:TOpenALSource
- Function CheckAL:Int()
- Local err:String
- 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:Int,_seq:Int,_sound:TOpenALSound,_avail:Int
-
- Method Playing:Int()
- Local st:Int
- alGetSourcei _id,AL_SOURCE_STATE,Varptr st
- Return st=AL_PLAYING
- End Method
-
- Method Paused:Int()
- Local st:Int
- alGetSourcei _id,AL_SOURCE_STATE,Varptr st
- Return st=AL_PAUSED Or st=AL_INITIAL
- End Method
-
- Method Active:Int()
- Local st:Int
- 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:Int
- 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:String[]()
- Local p:Byte Ptr=alcGetString(Null,ALC_DEVICE_SPECIFIER )
- If Not p Return Null
- Local devs:String[100],n:Int
- While p[0] And n<100
- Local sz:Int
- 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 ) Override
- Local t:TOpenALChannel=Cue( alloced_channel )
- t.SetPaused False
- Return t
- End Method
- Method Cue:TOpenALChannel( alloced_channel:TChannel=Null ) Override
- 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:Int )
- Local alfmt:Int
- 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:Int=-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:Int,_loop:Int
- 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() Override
- 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:Int ) Override
- If _seq<>_source._seq Return
- If paused
- alSourcePause _source._id
- Else
- If _source.Paused() alSourcePlay _source._id
- EndIf
- End Method
-
- Method SetVolume( volume:Float ) Override
- If _seq<>_source._seq Return
- alSourcef _source._id,AL_GAIN,volume
- End Method
-
- Method SetPan( pan:Float ) Override
- If _seq<>_source._seq Return
- pan:*90
- alSource3f _source._id,AL_POSITION,Float(Sin(pan)),0,Float(-Cos(pan))
- End Method
-
- Method SetDepth( depth:Float ) Override
- If _seq<>_source._seq Return
- End Method
-
- Method SetRate( rate:Float ) Override
- If _seq<>_source._seq Return
- alSourcef _source._id,AL_PITCH,rate
- End Method
-
- Method Playing:Int() Override
- If _seq<>_source._seq Return False
- 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:Int )
-
- 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:Int,_static:Int
-
- End Type
- Type TOpenALAudioDriver Extends TAudioDriver
- Method Name:String() Override
- Return _name
- End Method
-
- Method Startup:Int() Override
- _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() Override
- _sources=Null
- alcDestroyContext _context
- alcCloseDevice _device
- End Method
- Method CreateSound:TOpenALSound( sample:TAudioSample,flags:Int ) Override
- Return TOpenALSound.Create( sample,flags )
- End Method
-
- Method AllocChannel:TOpenALChannel() Override
- Return TOpenALChannel.Create( True )
- End Method
-
- Function Create:TOpenALAudioDriver( name:String,devname:String )
- Local t:TOpenALAudioDriver=New TOpenALAudioDriver
- t._name=name
- t._devname=devname
- Return t
- End Function
-
- Field _name:String,_devname:String,_device:Byte Ptr,_context:Byte Ptr
- 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:Int()
- Global done:Int,okay:Int
- If done Return okay
- If OpenALInstalled() And alcGetString
- For Local devname:String=EachIn EnumOpenALDevices()
- TOpenALAudioDriver.Create( "OpenAL "+devname,devname )
- Next
- TOpenALAudioDriver.Create "OpenAL Default",String.FromCString( alcGetString( Null,ALC_DEFAULT_DEVICE_SPECIFIER ) )
- okay=True
- EndIf
- done=True
- Return okay
- End Function
|