openalaudio.bmx 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. SuperStrict
  2. Rem
  3. bbdoc: Audio/OpenAL audio
  4. about:
  5. The OpenAL audio module provide OpenAL drivers for use with the #audio module.
  6. End Rem
  7. Module BRL.OpenALAudio
  8. ModuleInfo "Version: 1.05"
  9. ModuleInfo "Author: Mark Sibly"
  10. ModuleInfo "License: zlib/libpng"
  11. ModuleInfo "Copyright: Blitz Research Ltd"
  12. ModuleInfo "Modserver: BRL"
  13. ModuleInfo "History: 1.05 Release"
  14. ModuleInfo "History: Fixed default channel volume,pan,rate"
  15. ModuleInfo "History: Fixed behaviour of channel going out of scope"
  16. ModuleInfo "History: 1.04 Release"
  17. ModuleInfo "History: Fixed channel playing to return true if active (playing or paused)"
  18. ModuleInfo "History: 1.03 Release"
  19. ModuleInfo "History: Added (synced) alDeleteBuffers"
  20. ModuleInfo "History: 1.02 Release"
  21. ModuleInfo "History: Added EnableOpenALAudio"
  22. ModuleInfo "History: 1.01 Initial Release"
  23. Import BRL.Math
  24. Import BRL.Audio
  25. Import Pub.OpenAL
  26. Private
  27. Const CLOG:Int=False
  28. 'list of all non-static sources
  29. Global _sources:TOpenALSource
  30. Function CheckAL:Int()
  31. Local err:String
  32. Select alGetError()
  33. Case AL_NO_ERROR
  34. Return True
  35. Case AL_INVALID_NAME
  36. err="INVALID_NAME"
  37. Case AL_INVALID_ENUM
  38. err="INVALID_ENUM"
  39. Case AL_INVALID_VALUE
  40. err="INVALID_VALUE"
  41. Case AL_INVALID_OPERATION
  42. err="INVALID_OPERATION"
  43. Case AL_OUT_OF_MEMORY
  44. err="OUT_OF_MEMORY"
  45. Default
  46. err="?????"
  47. End Select
  48. If CLOG WriteStdout "OpenAL Error: "+err+"~n"
  49. Return False
  50. End Function
  51. Type TOpenALSource
  52. Field _succ:TOpenALSource,_id:Int,_seq:Int,_sound:TOpenALSound,_avail:Int
  53. Method Playing:Int()
  54. Local st:Int
  55. alGetSourcei _id,AL_SOURCE_STATE,Varptr st
  56. Return st=AL_PLAYING
  57. End Method
  58. Method Paused:Int()
  59. Local st:Int
  60. alGetSourcei _id,AL_SOURCE_STATE,Varptr st
  61. Return st=AL_PAUSED Or st=AL_INITIAL
  62. End Method
  63. Method Active:Int()
  64. Local st:Int
  65. alGetSourcei _id,AL_SOURCE_STATE,Varptr st
  66. Return st=AL_PLAYING Or st=AL_PAUSED Or st=AL_INITIAL
  67. End Method
  68. Method LogState()
  69. Local st:Int
  70. alGetSourcei _id,AL_SOURCE_STATE,Varptr st
  71. Select st
  72. Case AL_PAUSED WriteStdout "AL_PAUSED~n"
  73. Case AL_INITIAL WriteStdout "AL_INITIAL~n"
  74. Case AL_STOPPED WriteStdout "AL_STOPPED~n"
  75. Case AL_PLAYING WriteStdout "AL_PLAYING~n"
  76. Default WriteStdout "AL_DUNNO, st="+st+"~n"
  77. End Select
  78. End Method
  79. End Type
  80. Function EnumOpenALDevices:String[]()
  81. Local p:Byte Ptr=alcGetString(Null,ALC_DEVICE_SPECIFIER )
  82. If Not p Return Null
  83. Local devs:String[100],n:Int
  84. While p[0] And n<100
  85. Local sz:Int
  86. Repeat
  87. sz:+1
  88. Until Not p[sz]
  89. devs[n]=String.FromBytes( p,sz )
  90. n:+1
  91. p:+sz+1
  92. Wend
  93. Return devs[..n]
  94. End Function
  95. Public
  96. Type TOpenALSound Extends TSound
  97. Method Delete()
  98. alDeleteBuffers 1,Varptr _buffer
  99. CheckAL
  100. If CLOG WriteStdout "Deleted OpenAL buffer "+_buffer+"~n"
  101. End Method
  102. Method Play:TOpenALChannel( alloced_channel:TChannel=Null ) Override
  103. Local t:TOpenALChannel=Cue( alloced_channel )
  104. t.SetPaused False
  105. Return t
  106. End Method
  107. Method Cue:TOpenALChannel( alloced_channel:TChannel=Null ) Override
  108. Local t:TOpenALChannel=TOpenALChannel( alloced_channel )
  109. If t
  110. Assert t._static
  111. Else
  112. t=TOpenALChannel.Create( False )
  113. EndIf
  114. t.Cue Self
  115. Return t
  116. End Method
  117. Function Create:TOpenALSound( sample:TAudioSample,flags:Int )
  118. Local alfmt:Int
  119. Select sample.format
  120. Case SF_MONO8
  121. alfmt=AL_FORMAT_MONO8
  122. Case SF_MONO16LE
  123. alfmt=AL_FORMAT_MONO16
  124. ?BigEndian
  125. sample=sample.Convert( SF_MONO16BE )
  126. ?
  127. Case SF_MONO16BE
  128. alfmt=AL_FORMAT_MONO16
  129. ?LittleEndian
  130. sample=sample.Convert( SF_MONO16LE )
  131. ?
  132. Case SF_STEREO8
  133. alfmt=AL_FORMAT_STEREO8
  134. Case SF_STEREO16LE
  135. alfmt=AL_FORMAT_STEREO16
  136. ?BigEndian
  137. sample=sample.Convert( SF_STEREO16BE )
  138. ?
  139. Case SF_STEREO16BE
  140. alfmt=AL_FORMAT_STEREO16
  141. ?LittleEndian
  142. sample=sample.Convert( SF_STEREO16LE )
  143. ?
  144. End Select
  145. Local buffer:Int=-1
  146. alGenBuffers 1,Varptr buffer
  147. CheckAL
  148. If CLOG WriteStdout "Generated OpenAL buffer "+buffer+"~n"
  149. alBufferData buffer,alfmt,sample.samples,sample.length*BytesPerSample[sample.format],sample.hertz
  150. CheckAL
  151. Local t:TOpenALSound=New TOpenALSound
  152. t._buffer=buffer
  153. If (flags & 1) t._loop=1
  154. Return t
  155. End Function
  156. Field _buffer:Int,_loop:Int
  157. End Type
  158. Type TOpenALChannel Extends TChannel
  159. Method Delete()
  160. If _seq<>_source._seq Return
  161. If _static 'LEAKED!
  162. Stop 'Just in case...
  163. Return
  164. EndIf
  165. If Not _source.Playing()
  166. alSourceStop _source._id
  167. alSourcei _source._id,AL_BUFFER,0
  168. _source._sound=Null
  169. EndIf
  170. End Method
  171. Method Stop() Override
  172. If _seq<>_source._seq Return
  173. _source._seq:+1
  174. alSourceStop _source._id
  175. alSourcei _source._id,AL_BUFFER,0
  176. _source._sound=Null
  177. If _static
  178. _source._avail=True
  179. _source._succ=_sources
  180. _sources=_source
  181. EndIf
  182. End Method
  183. Method SetPaused( paused:Int ) Override
  184. If _seq<>_source._seq Return
  185. If paused
  186. alSourcePause _source._id
  187. Else
  188. If _source.Paused() alSourcePlay _source._id
  189. EndIf
  190. End Method
  191. Method SetVolume( volume:Float ) Override
  192. If _seq<>_source._seq Return
  193. alSourcef _source._id,AL_GAIN,volume
  194. End Method
  195. Method SetPan( pan:Float ) Override
  196. If _seq<>_source._seq Return
  197. pan:*90
  198. alSource3f _source._id,AL_POSITION,Float(Sin(pan)),0,Float(-Cos(pan))
  199. End Method
  200. Method SetDepth( depth:Float ) Override
  201. If _seq<>_source._seq Return
  202. End Method
  203. Method SetRate( rate:Float ) Override
  204. If _seq<>_source._seq Return
  205. alSourcef _source._id,AL_PITCH,rate
  206. End Method
  207. Method Playing:Int() Override
  208. If _seq<>_source._seq Return False
  209. Return _source.Playing()
  210. End Method
  211. Method Cue( sound:TOpenALSound )
  212. If _seq<>_source._seq Return
  213. _source._sound=sound
  214. alSourceRewind _source._id
  215. alSourcei _source._id,AL_LOOPING,sound._loop
  216. alSourcei _source._id,AL_BUFFER,sound._buffer
  217. End Method
  218. Function Create:TOpenALChannel( static:Int )
  219. Local source:TOpenALSource=_sources,pred:TOpenALSource
  220. While source
  221. If source._avail Or Not source.Active()
  222. source._seq:+1
  223. If pred pred._succ=source._succ Else _sources=source._succ
  224. If CLOG WriteStdout "Recycling OpenAL source "+source._id+"~n"
  225. If CLOG source.LogState
  226. alSourceRewind source._id 'return to FA_INITIAL state
  227. If CLOG source.LogState
  228. alSourcei source._id,AL_BUFFER,0
  229. source._sound=Null
  230. Exit
  231. EndIf
  232. pred=source
  233. source=source._succ
  234. Wend
  235. If Not source
  236. source=New TOpenALSource
  237. alGenSources 1,Varptr source._id
  238. CheckAL
  239. If source._id
  240. If CLOG WriteStdout "Generated OpenAL source "+source._id+"~n"
  241. Else
  242. If CLOG WriteStdout "Failed to generate OpenAL source~n"
  243. EndIf
  244. EndIf
  245. Local t:TOpenALChannel=New TOpenALChannel
  246. t._source=source
  247. t._seq=source._seq
  248. t._static=static
  249. If source._id
  250. alSourcei source._id,AL_SOURCE_RELATIVE,True
  251. alSourcef source._id,AL_GAIN,1
  252. alSourcef source._id,AL_PITCH,1
  253. alSource3f source._id,AL_POSITION,0,0,1
  254. If Not static
  255. source._avail=False
  256. source._succ=_sources
  257. _sources=source
  258. EndIf
  259. Else
  260. t._seq:+1
  261. EndIf
  262. Return t
  263. End Function
  264. Field _source:TOpenALSource,_seq:Int,_static:Int
  265. End Type
  266. Type TOpenALAudioDriver Extends TAudioDriver
  267. Method Name:String() Override
  268. Return _name
  269. End Method
  270. Method Startup:Int() Override
  271. _device=0
  272. If _devname
  273. _device=alcOpenDevice( _devname )
  274. Else If OpenALInstalled()
  275. _device=alcOpenDevice( Null )
  276. If Not _device
  277. _device=alcOpenDevice( "Generic Hardware" )
  278. If Not _device
  279. _device=alcOpenDevice( "Generic Software" )
  280. EndIf
  281. EndIf
  282. EndIf
  283. If _device
  284. _context=alcCreateContext( _device,Null )
  285. If _context
  286. alcMakeContextCurrent _context
  287. alDistanceModel AL_NONE
  288. Return True
  289. EndIf
  290. alcCloseDevice( _device )
  291. EndIf
  292. End Method
  293. Method Shutdown() Override
  294. _sources=Null
  295. alcDestroyContext _context
  296. alcCloseDevice _device
  297. End Method
  298. Method CreateSound:TOpenALSound( sample:TAudioSample,flags:Int ) Override
  299. Return TOpenALSound.Create( sample,flags )
  300. End Method
  301. Method AllocChannel:TOpenALChannel() Override
  302. Return TOpenALChannel.Create( True )
  303. End Method
  304. Function Create:TOpenALAudioDriver( name:String,devname:String )
  305. Local t:TOpenALAudioDriver=New TOpenALAudioDriver
  306. t._name=name
  307. t._devname=devname
  308. Return t
  309. End Function
  310. Field _name:String,_devname:String,_device:Byte Ptr,_context:Byte Ptr
  311. End Type
  312. If OpenALInstalled() TOpenALAudioDriver.Create "OpenAL",""
  313. Rem
  314. bbdoc: Enable OpenAL Audio
  315. returns: True if successful
  316. about:
  317. After successfully executing this command, OpenAL audio drivers will be added
  318. to the array of drivers returned by #AudioDrivers.
  319. End Rem
  320. Function EnableOpenALAudio:Int()
  321. Global done:Int,okay:Int
  322. If done Return okay
  323. If OpenALInstalled() And alcGetString
  324. For Local devname:String=EachIn EnumOpenALDevices()
  325. TOpenALAudioDriver.Create( "OpenAL "+devname,devname )
  326. Next
  327. TOpenALAudioDriver.Create "OpenAL Default",String.FromCString( alcGetString( Null,ALC_DEFAULT_DEVICE_SPECIFIER ) )
  328. okay=True
  329. EndIf
  330. done=True
  331. Return okay
  332. End Function