soloudaudio.bmx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494
  1. ' Copyright (c) 2016-2022 Bruce A Henderson
  2. '
  3. ' This software is provided 'as-is', without any express or implied
  4. ' warranty. In no event will the authors be held liable for any damages
  5. ' arising from the use of this software.
  6. '
  7. ' Permission is granted to anyone to use this software for any purpose,
  8. ' including commercial applications, and to alter it and redistribute it
  9. ' freely, subject to the following restrictions:
  10. '
  11. ' 1. The origin of this software must not be misrepresented; you must not
  12. ' claim that you wrote the original software. If you use this software
  13. ' in a product, an acknowledgment in the product documentation would be
  14. ' appreciated but is not required.
  15. '
  16. ' 2. Altered source versions must be plainly marked as such, and must not be
  17. ' misrepresented as being the original software.
  18. '
  19. ' 3. This notice may not be removed or altered from any source
  20. ' distribution.
  21. '
  22. SuperStrict
  23. Rem
  24. bbdoc: Soloud Audio Driver
  25. about: Provides Soloud driver for use with the BRL.Audio module.
  26. End Rem
  27. Module Audio.soloudaudio
  28. ModuleInfo "Version: 1.02"
  29. ModuleInfo "License: zlib/libpng"
  30. ModuleInfo "Copyright: 2016-2022 Bruce A Henderson"
  31. ModuleInfo "History: 1.02"
  32. ModuleInfo "History: Fixed loader priority ordering."
  33. ModuleInfo "History: 1.01"
  34. ModuleInfo "History: Added support for Ay (in .zap format) loading."
  35. ModuleInfo "History: 1.00"
  36. ModuleInfo "History: Initial Release."
  37. Import Audio.soloud
  38. Import BRL.Audio
  39. Import brl.filesystem
  40. Private
  41. Global _driver:TSoloudAudioDriver
  42. Public
  43. Const SOLOUD_SOUND_WAV:Int = $00001000
  44. Const SOLOUD_SOUND_WAVSTREAM:Int = $00002000
  45. Const SOLOUD_SOUND_SFXR:Int = $00004000
  46. Const SOLOUD_SOUND_OPENMPT:Int = $00008000
  47. Const SOLOUD_SOUND_MONOTONE:Int = $00010000
  48. Const SOLOUD_SOUND_TEDSID:Int = $00020000
  49. Const SOLOUD_SOUND_SPEECH:Int = $00040000
  50. Const SOLOUD_SOUND_AY:Int = $00080000
  51. Const SOLOUD_SOUND_QUEUED:Int = $00800000
  52. Const SOLOUD_SOUND_PAUSE_INAUDIBLE:Int = $10000000
  53. Const SOLOUD_SOUND_PROTECT:Int = $20000000
  54. Type TSoloudAudioDriver Extends TAudioDriver
  55. Field _soloud:TSoloud
  56. Method Name:String()
  57. Return "SoLoud"
  58. End Method
  59. Method Startup:Int()
  60. _soloud = New TSoloud
  61. _soloud.init(, Backend())
  62. _driver = Self
  63. Return True
  64. End Method
  65. Method Shutdown()
  66. Local soloud:TSoloud = _soloud
  67. _soloud = Null
  68. soloud.deinit()
  69. _driver = Null
  70. End Method
  71. Method CreateSound:TSound( sample:TAudioSample, loopFlag:Int )
  72. 'Return New TSound
  73. End Method
  74. Method LoadSound:TSound( url:Object, flags:Int = 0)
  75. Return TSoloudSound.Load(url, flags)
  76. End Method
  77. Method AllocChannel:TChannel()
  78. Return New TSoloudChannel
  79. End Method
  80. Method Backend:Int() Abstract
  81. End Type
  82. Type TSoloudSound Extends TSound
  83. Field _sound:TSLAudioSource
  84. Field isLooped:Int
  85. Field pauseInaudible:Int
  86. Field isProtected:Int
  87. Method Play:TChannel( allocedChannel:TChannel=Null )
  88. Return StartSound(allocedChannel, False)
  89. End Method
  90. Method Cue:TChannel( allocedChannel:TChannel=Null )
  91. Return StartSound(allocedChannel, True)
  92. End Method
  93. Method StartSound:TChannel(allocedChannel:TChannel=Null, pause:Int = False)
  94. If isLooped Then
  95. _sound.SetLooping(True)
  96. End If
  97. If Not pauseInaudible
  98. _sound.setInaudibleBehavior(True, False)
  99. End If
  100. Local voiceHandle:Int = _driver._soloud.play(_sound, -1, 0, True)
  101. Local channel:TChannel
  102. If Not allocedChannel Then
  103. channel = New TSoloudChannel.Create(_driver._soloud, voiceHandle, _sound)
  104. Else
  105. TSoloudChannel(allocedChannel).Set(_driver._soloud, voiceHandle, _sound)
  106. channel = allocedChannel
  107. End If
  108. If isProtected Then
  109. TSoloudChannel(channel).SetProtected(isProtected)
  110. End If
  111. If Not pause Then
  112. channel.SetPaused(False)
  113. End If
  114. Return channel
  115. End Method
  116. Function Load:TSound( url:Object, loopFlag:Int )
  117. If loopFlag & SOLOUD_SOUND_SPEECH Then
  118. Local this:TSoloudSound = New TSoloudSound
  119. this._sound = New TSLSpeech
  120. TSLSpeech(this._sound).SetText(String(url))
  121. If loopFlag & SOUND_LOOP Then
  122. this.isLooped = True
  123. End If
  124. Return this
  125. End If
  126. If loopFlag & SOLOUD_SOUND_QUEUED Then
  127. Local this:TSoloudSound = New TSoloudSound
  128. this._sound = New TSLQueued
  129. If loopFlag & SOUND_LOOP Then
  130. this.isLooped = True
  131. End If
  132. Return this
  133. End If
  134. Local sound:TSLLoadableAudioSource
  135. If loopFlag & SOLOUD_SOUND_WAV Then
  136. sound = New TSLWav
  137. Else If loopFlag & SOLOUD_SOUND_WAVSTREAM Then
  138. sound = New TSLWavStream
  139. Else If loopFlag & SOLOUD_SOUND_SFXR Then
  140. sound = New TSLSfxr
  141. Else If loopFlag & SOLOUD_SOUND_MONOTONE Then
  142. sound = New TSLMonotone
  143. Else If loopFlag & SOLOUD_SOUND_TEDSID Then
  144. sound = New TSLTedSid
  145. Else If loopFlag & SOLOUD_SOUND_AY Then
  146. sound = New TSLAy
  147. End If
  148. Local this:TSoloudSound = New TSoloudSound
  149. If sound Then
  150. this._sound = sound
  151. End If
  152. If loopFlag & SOUND_LOOP Then
  153. this.isLooped = True
  154. End If
  155. If loopFlag & SOLOUD_SOUND_PAUSE_INAUDIBLE Then
  156. this.pauseInaudible = True
  157. End If
  158. Local stream:TStream
  159. If String(url) Then
  160. stream = ReadStream(url)
  161. If Not stream Then
  162. Return Null
  163. End If
  164. Else If TStream(url) Then
  165. stream = TStream(url)
  166. End If
  167. If stream Then
  168. If Not sound Then
  169. this._sound = TryLoadSound(stream, loopFlag)
  170. If Not this._sound Then
  171. Return Null
  172. End If
  173. Return this
  174. End If
  175. If sound Then
  176. Local res:Int = sound.loadStream(stream)
  177. If res Then
  178. Return Null
  179. End If
  180. Return this
  181. End If
  182. End If
  183. Return this
  184. End Function
  185. Function TryLoadSound:TSLLoadableAudioSource(stream:TStream, flags:Int)
  186. If Not stream Then Return Null
  187. Local sound:TSLLoadableAudioSource
  188. Local loader:TAudioSourceLoader = audio_loaders
  189. Local pos:Int = stream.Pos()
  190. While loader
  191. 'reset to initial position for each loader attempt
  192. stream.Seek(pos)
  193. sound = loader.LoadAudioSource(stream, flags)
  194. If sound Then Exit
  195. loader = loader._succ
  196. Wend
  197. Return sound
  198. End Function
  199. End Type
  200. Type TSoloudChannel Extends TChannel
  201. Field _soloud:TSoloud
  202. Field _voiceHandle:Int
  203. Field _sound:TSLAudioSource
  204. ' since we can "alloc" a channel, it won't yet have a _channel object.. which will be added later.
  205. ' so, any settings changed need to be cached and applied when initialized
  206. Field prePaused:Int
  207. Field preVolume:Float
  208. Field prePan:Float
  209. Field preRate:Float
  210. Field preflags:Int
  211. Field APPLY_PAUSED:Int = 1
  212. Field APPLY_VOLUME:Int = 2
  213. Field APPLY_PAN:Int = 4
  214. Field APPLY_RATE:Int = 8
  215. Method Create:TSoloudChannel(soloud:TSoloud, voiceHandle:Int, sound:TSLAudioSource)
  216. _soloud = soloud
  217. _voiceHandle = voiceHandle
  218. _sound = sound
  219. Return Self
  220. End Method
  221. ' - usually applied if this object was created with AllocChannel()
  222. Method Set(soloud:TSoloud, voiceHandle:Int, sound:TSLAudioSource)
  223. _soloud = soloud
  224. _voiceHandle = voiceHandle
  225. _sound = sound
  226. If preflags & APPLY_PAUSED Then
  227. SetPaused(prePaused)
  228. End If
  229. If preflags & APPLY_VOLUME Then
  230. SetVolume(preVolume)
  231. End If
  232. If preflags & APPLY_PAN Then
  233. SetPan(prePan)
  234. End If
  235. If preflags & APPLY_RATE Then
  236. SetRate(preRate)
  237. End If
  238. End Method
  239. Method Stop() Override
  240. If _voiceHandle Then
  241. _soloud.stop(_voiceHandle)
  242. End If
  243. End Method
  244. Method SetPaused( paused:Int ) Override
  245. If _voiceHandle Then
  246. _soloud.setPause(_voiceHandle, paused)
  247. Else
  248. preflags:| APPLY_PAUSED
  249. prePaused = paused
  250. End If
  251. End Method
  252. Method SetVolume( volume:Float ) Override
  253. If _voiceHandle Then
  254. _soloud.setVolume(_voiceHandle, volume)
  255. End If
  256. preflags:| APPLY_VOLUME
  257. preVolume = volume
  258. End Method
  259. Method SetPan( pan:Float ) Override
  260. If _voiceHandle Then
  261. _soloud.setPan(_voiceHandle, pan)
  262. End If
  263. preflags:| APPLY_PAN
  264. prePan = pan
  265. End Method
  266. Method SetDepth( depth:Float ) Override
  267. ' TODO ?
  268. End Method
  269. Method SetRate( rate:Float ) Override
  270. If _voiceHandle Then
  271. _soloud.setSamplerate(_voiceHandle, rate)
  272. End If
  273. preflags:| APPLY_RATE
  274. preRate = rate
  275. End Method
  276. Method Playing:Int() Override
  277. If _voiceHandle Then
  278. Return _soloud.isValidVoiceHandle(_voiceHandle) And Not _soloud.getPause(_voiceHandle)
  279. Else
  280. Return False
  281. End If
  282. End Method
  283. Method Position:Int()
  284. If _voiceHandle Then
  285. Return Int(_soloud.getStreamPosition(_voiceHandle) * 1000)
  286. Else
  287. Return 0
  288. End If
  289. End Method
  290. Method StreamTime:Int()
  291. If _voiceHandle Then
  292. Return Int(_soloud.getStreamTime(_voiceHandle) * 1000)
  293. Else
  294. Return 0
  295. End If
  296. End Method
  297. Method LoopCount:Int()
  298. If _voiceHandle Then
  299. Return _soloud.getLoopCount(_voiceHandle)
  300. Else
  301. Return 0
  302. End If
  303. End Method
  304. Method Length:Int()
  305. If _sound Then
  306. Return Int(_sound.getLength() * 1000)
  307. Else
  308. Return -1
  309. End If
  310. End Method
  311. Method SetProtected(protect:Int)
  312. If _voiceHandle Then
  313. _soloud.setProtectVoice(_voiceHandle, protect)
  314. End If
  315. End Method
  316. End Type
  317. Private
  318. Global audio_loaders:TAudioSourceLoader
  319. Public
  320. New TSoloudWavLoader(5)
  321. New TSoloudMonotoneLoader(10)
  322. New TSoloudAyLoader(15)
  323. Rem
  324. bbdoc:
  325. End Rem
  326. Type TAudioSourceLoader
  327. Field _succ:TAudioSourceLoader
  328. Field _priority:Int
  329. Method New(priority:Int)
  330. Self._priority = priority
  331. Local loader:TAudioSourceLoader = audio_loaders
  332. Local last:TAudioSourceLoader
  333. While loader
  334. If priority <= loader._priority Then
  335. _succ=audio_loaders
  336. audio_loaders=Self
  337. Return
  338. End If
  339. last = loader
  340. loader = loader._succ
  341. Wend
  342. If last Then
  343. If last._succ Then Throw "Unexpected audio loader entry"
  344. last._succ = Self
  345. Else
  346. audio_loaders = Self
  347. End If
  348. End Method
  349. Rem
  350. bbdoc: Load an audio sample
  351. returns: A new audio sample object, or Null if sample could not be loaded
  352. about: Extending types must implement this method.
  353. End Rem
  354. Method LoadAudioSource:TSLLoadableAudioSource( stream:TStream, flags:Int ) Abstract
  355. End Type
  356. Type TSoloudWavLoader Extends TAudioSourceLoader
  357. Method LoadAudioSource:TSLLoadableAudioSource( stream:TStream, flags:Int )
  358. Local sound:TSLLoadableAudioSource
  359. If flags & SOUND_STREAM Then
  360. sound = New TSLWavStream
  361. If sound.loadStream(stream) = SO_NO_ERROR Then
  362. Return sound
  363. End If
  364. sound.destroy()
  365. End If
  366. sound = New TSLWav
  367. If sound.loadStream(stream) = SO_NO_ERROR Then
  368. Return sound
  369. End If
  370. sound.destroy()
  371. End Method
  372. End Type
  373. Type TSoloudMonotoneLoader Extends TAudioSourceLoader
  374. Method LoadAudioSource:TSLLoadableAudioSource( stream:TStream, flags:Int )
  375. Local sound:TSLLoadableAudioSource = New TSLMonotone
  376. If sound.loadStream(stream) = SO_NO_ERROR Then
  377. Return sound
  378. End If
  379. sound.destroy()
  380. End Method
  381. End Type
  382. Type TSoloudAyLoader Extends TAudioSourceLoader
  383. Method LoadAudioSource:TSLLoadableAudioSource( stream:TStream, flags:Int )
  384. Local sound:TSLLoadableAudioSource = New TSLAy
  385. If sound.loadStream(stream) = SO_NO_ERROR Then
  386. Return sound
  387. End If
  388. sound.destroy()
  389. End Method
  390. End Type