openalaudio.bmx 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  1. Strict
  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=False
  28. 'list of all non-static sources
  29. Global _sources:TOpenALSource
  30. Function CheckAL()
  31. Local err$
  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,_seq,_sound:TOpenALSound,_avail
  53. Method Playing()
  54. Local st
  55. alGetSourcei _id,AL_SOURCE_STATE,Varptr st
  56. Return st=AL_PLAYING
  57. End Method
  58. Method Paused()
  59. Local st
  60. alGetSourcei _id,AL_SOURCE_STATE,Varptr st
  61. Return st=AL_PAUSED Or st=AL_INITIAL
  62. End Method
  63. Method Active()
  64. Local st
  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
  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$[]()
  81. Local p:Byte Ptr=alcGetString( 0,ALC_DEVICE_SPECIFIER )
  82. If Not p Return
  83. Local devs$[100],n
  84. While p[0] And n<100
  85. Local sz
  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 )
  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 )
  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 )
  118. Local alfmt
  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=-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,_loop
  157. End Type
  158. Rem
  159. bbdoc: OpenAL audio channel Type
  160. End Rem
  161. Type TOpenALChannel Extends TChannel
  162. Method Delete()
  163. If _seq<>_source._seq Return
  164. If _static 'LEAKED!
  165. Stop 'Just in case...
  166. Return
  167. EndIf
  168. If Not _source.Playing()
  169. alSourceStop _source._id
  170. alSourcei _source._id,AL_BUFFER,0
  171. _source._sound=Null
  172. EndIf
  173. End Method
  174. Method Stop()
  175. If _seq<>_source._seq Return
  176. _source._seq:+1
  177. alSourceStop _source._id
  178. alSourcei _source._id,AL_BUFFER,0
  179. _source._sound=Null
  180. If _static
  181. _source._avail=True
  182. _source._succ=_sources
  183. _sources=_source
  184. EndIf
  185. End Method
  186. Method SetPaused( paused )
  187. If _seq<>_source._seq Return
  188. If paused
  189. alSourcePause _source._id
  190. Else
  191. If _source.Paused() alSourcePlay _source._id
  192. EndIf
  193. End Method
  194. Method SetVolume( volume# )
  195. If _seq<>_source._seq Return
  196. alSourcef _source._id,AL_GAIN,volume
  197. End Method
  198. Method SetPan( pan# )
  199. If _seq<>_source._seq Return
  200. pan:*90
  201. alSource3f _source._id,AL_POSITION,Sin(pan),0,-Cos(pan)
  202. End Method
  203. Method SetDepth( depth# )
  204. If _seq<>_source._seq Return
  205. End Method
  206. Method SetRate( rate# )
  207. If _seq<>_source._seq Return
  208. alSourcef _source._id,AL_PITCH,rate
  209. End Method
  210. Method Playing()
  211. If _seq<>_source._seq Return
  212. Return _source.Playing()
  213. End Method
  214. Method Cue( sound:TOpenALSound )
  215. If _seq<>_source._seq Return
  216. _source._sound=sound
  217. alSourceRewind _source._id
  218. alSourcei _source._id,AL_LOOPING,sound._loop
  219. alSourcei _source._id,AL_BUFFER,sound._buffer
  220. End Method
  221. Rem
  222. bbdoc: Get native openal source id
  223. returns: The native openal source id for the channel, or 0 if the channel
  224. has been stopped using #Stop.
  225. End Rem
  226. Method OpenALSourceId()
  227. If _seq<>_source._seq Return 0
  228. Return _source._id
  229. End Method
  230. Function Create:TOpenALChannel( static )
  231. Local source:TOpenALSource=_sources,pred:TOpenALSource
  232. While source
  233. If source._avail Or Not source.Active()
  234. source._seq:+1
  235. If pred pred._succ=source._succ Else _sources=source._succ
  236. If CLOG WriteStdout "Recycling OpenAL source "+source._id+"~n"
  237. If CLOG source.LogState
  238. alSourceRewind source._id 'return to FA_INITIAL state
  239. If CLOG source.LogState
  240. alSourcei source._id,AL_BUFFER,0
  241. source._sound=Null
  242. Exit
  243. EndIf
  244. pred=source
  245. source=source._succ
  246. Wend
  247. If Not source
  248. source=New TOpenALSource
  249. alGenSources 1,Varptr source._id
  250. CheckAL
  251. If source._id
  252. If CLOG WriteStdout "Generated OpenAL source "+source._id+"~n"
  253. Else
  254. If CLOG WriteStdout "Failed to generate OpenAL source~n"
  255. EndIf
  256. EndIf
  257. Local t:TOpenALChannel=New TOpenALChannel
  258. t._source=source
  259. t._seq=source._seq
  260. t._static=static
  261. If source._id
  262. alSourcei source._id,AL_SOURCE_RELATIVE,True
  263. alSourcef source._id,AL_GAIN,1
  264. alSourcef source._id,AL_PITCH,1
  265. alSource3f source._id,AL_POSITION,0,0,1
  266. If Not static
  267. source._avail=False
  268. source._succ=_sources
  269. _sources=source
  270. EndIf
  271. Else
  272. t._seq:+1
  273. EndIf
  274. Return t
  275. End Function
  276. Field _source:TOpenALSource,_seq,_static
  277. End Type
  278. Type TOpenALAudioDriver Extends TAudioDriver
  279. Method Name$()
  280. Return _name
  281. End Method
  282. Method Startup()
  283. _device=0
  284. If _devname
  285. _device=alcOpenDevice( _devname )
  286. Else If OpenALInstalled()
  287. _device=alcOpenDevice( Null )
  288. If Not _device
  289. _device=alcOpenDevice( "Generic Hardware" )
  290. If Not _device
  291. _device=alcOpenDevice( "Generic Software" )
  292. EndIf
  293. EndIf
  294. EndIf
  295. If _device
  296. _context=alcCreateContext( _device,Null )
  297. If _context
  298. alcMakeContextCurrent _context
  299. alDistanceModel AL_NONE
  300. Return True
  301. EndIf
  302. alcCloseDevice( _device )
  303. EndIf
  304. End Method
  305. Method Shutdown()
  306. _sources=Null
  307. alcDestroyContext _context
  308. alcCloseDevice _device
  309. End Method
  310. Method CreateSound:TOpenALSound( sample:TAudioSample,flags )
  311. Return TOpenALSound.Create( sample,flags )
  312. End Method
  313. Method AllocChannel:TOpenALChannel()
  314. Return TOpenALChannel.Create( True )
  315. End Method
  316. Function Create:TOpenALAudioDriver( name$,devname$ )
  317. Local t:TOpenALAudioDriver=New TOpenALAudioDriver
  318. t._name=name
  319. t._devname=devname
  320. Return t
  321. End Function
  322. Field _name$,_devname$,_device,_context
  323. End Type
  324. If OpenALInstalled() TOpenALAudioDriver.Create "OpenAL",""
  325. Rem
  326. bbdoc: Enable OpenAL Audio
  327. returns: True if successful
  328. about:
  329. After successfully executing this command, OpenAL audio drivers will be added
  330. to the array of drivers returned by #AudioDrivers.
  331. End Rem
  332. Function EnableOpenALAudio()
  333. Global done,okay
  334. If done Return okay
  335. If OpenALInstalled() And alcGetString
  336. For Local devname$=EachIn EnumOpenALDevices()
  337. TOpenALAudioDriver.Create( "OpenAL "+devname,devname )
  338. Next
  339. TOpenALAudioDriver.Create "OpenAL Default",String.FromCString( alcGetString( 0,ALC_DEFAULT_DEVICE_SPECIFIER ) )
  340. okay=True
  341. EndIf
  342. done=True
  343. Return okay
  344. End Function