soloudaudio.bmx 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482
  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_PAUSE_INAUDIBLE:Int = $10000000
  52. Const SOLOUD_SOUND_PROTECT:Int = $20000000
  53. Type TSoloudAudioDriver Extends TAudioDriver
  54. Field _soloud:TSoloud
  55. Method Name:String()
  56. Return "SoLoud"
  57. End Method
  58. Method Startup:Int()
  59. _soloud = New TSoloud
  60. _soloud.init(, Backend())
  61. _driver = Self
  62. Return True
  63. End Method
  64. Method Shutdown()
  65. Local soloud:TSoloud = _soloud
  66. _soloud = Null
  67. soloud.deinit()
  68. _driver = Null
  69. End Method
  70. Method CreateSound:TSound( sample:TAudioSample, loopFlag:Int )
  71. 'Return New TSound
  72. End Method
  73. Method LoadSound:TSound( url:Object, flags:Int = 0)
  74. Return TSoloudSound.Load(url, flags)
  75. End Method
  76. Method AllocChannel:TChannel()
  77. Return New TSoloudChannel
  78. End Method
  79. Method Backend:Int() Abstract
  80. End Type
  81. Type TSoloudSound Extends TSound
  82. Field _sound:TSLAudioSource
  83. Field isLooped:Int
  84. Field pauseInaudible:Int
  85. Field isProtected:Int
  86. Method Play:TChannel( allocedChannel:TChannel=Null )
  87. Return StartSound(allocedChannel, False)
  88. End Method
  89. Method Cue:TChannel( allocedChannel:TChannel=Null )
  90. Return StartSound(allocedChannel, True)
  91. End Method
  92. Method StartSound:TChannel(allocedChannel:TChannel=Null, pause:Int = False)
  93. If isLooped Then
  94. _sound.SetLooping(True)
  95. End If
  96. If Not pauseInaudible
  97. _sound.setInaudibleBehavior(True, False)
  98. End If
  99. Local voiceHandle:Int = _driver._soloud.play(_sound, -1, 0, True)
  100. Local channel:TChannel
  101. If Not allocedChannel Then
  102. channel = New TSoloudChannel.Create(_driver._soloud, voiceHandle, _sound)
  103. Else
  104. TSoloudChannel(allocedChannel).Set(_driver._soloud, voiceHandle, _sound)
  105. channel = allocedChannel
  106. End If
  107. If isProtected Then
  108. TSoloudChannel(channel).SetProtected(isProtected)
  109. End If
  110. If Not pause Then
  111. channel.SetPaused(False)
  112. End If
  113. Return channel
  114. End Method
  115. Function Load:TSound( url:Object, loopFlag:Int )
  116. If loopFlag & SOLOUD_SOUND_SPEECH Then
  117. Local this:TSoloudSound = New TSoloudSound
  118. this._sound = New TSLSpeech
  119. TSLSpeech(this._sound).SetText(String(url))
  120. If loopFlag & SOUND_LOOP Then
  121. this.isLooped = True
  122. End If
  123. Return this
  124. End If
  125. Local sound:TSLLoadableAudioSource
  126. If loopFlag & SOLOUD_SOUND_WAV Then
  127. sound = New TSLWav
  128. Else If loopFlag & SOLOUD_SOUND_WAVSTREAM Then
  129. sound = New TSLWavStream
  130. Else If loopFlag & SOLOUD_SOUND_SFXR Then
  131. sound = New TSLSfxr
  132. Else If loopFlag & SOLOUD_SOUND_MONOTONE Then
  133. sound = New TSLMonotone
  134. Else If loopFlag & SOLOUD_SOUND_TEDSID Then
  135. sound = New TSLTedSid
  136. Else If loopFlag & SOLOUD_SOUND_AY Then
  137. sound = New TSLAy
  138. End If
  139. Local this:TSoloudSound = New TSoloudSound
  140. If sound Then
  141. this._sound = sound
  142. End If
  143. If loopFlag & SOUND_LOOP Then
  144. this.isLooped = True
  145. End If
  146. If loopFlag & SOLOUD_SOUND_PAUSE_INAUDIBLE Then
  147. this.pauseInaudible = True
  148. End If
  149. Local stream:TStream
  150. If String(url) Then
  151. stream = ReadStream(url)
  152. If Not stream Then
  153. Return Null
  154. End If
  155. Else If TStream(url) Then
  156. stream = TStream(url)
  157. End If
  158. If stream Then
  159. If Not sound Then
  160. this._sound = TryLoadSound(stream, loopFlag)
  161. If Not this._sound Then
  162. Return Null
  163. End If
  164. Return this
  165. End If
  166. If sound Then
  167. Local res:Int = sound.loadStream(stream)
  168. If res Then
  169. Return Null
  170. End If
  171. Return this
  172. End If
  173. End If
  174. Return this
  175. End Function
  176. Function TryLoadSound:TSLLoadableAudioSource(stream:TStream, flags:Int)
  177. If Not stream Then Return Null
  178. Local sound:TSLLoadableAudioSource
  179. Local loader:TAudioSourceLoader = audio_loaders
  180. Local pos:Int = stream.Pos()
  181. While loader
  182. 'reset to initial position for each loader attempt
  183. stream.Seek(pos)
  184. sound = loader.LoadAudioSource(stream, flags)
  185. If sound Then Exit
  186. loader = loader._succ
  187. Wend
  188. Return sound
  189. End Function
  190. End Type
  191. Type TSoloudChannel Extends TChannel
  192. Field _soloud:TSoloud
  193. Field _voiceHandle:Int
  194. Field _sound:TSLAudioSource
  195. ' since we can "alloc" a channel, it won't yet have a _channel object.. which will be added later.
  196. ' so, any settings changed need to be cached and applied when initialized
  197. Field prePaused:Int
  198. Field preVolume:Float
  199. Field prePan:Float
  200. Field preRate:Float
  201. Field preflags:Int
  202. Field APPLY_PAUSED:Int = 1
  203. Field APPLY_VOLUME:Int = 2
  204. Field APPLY_PAN:Int = 4
  205. Field APPLY_RATE:Int = 8
  206. Method Create:TSoloudChannel(soloud:TSoloud, voiceHandle:Int, sound:TSLAudioSource)
  207. _soloud = soloud
  208. _voiceHandle = voiceHandle
  209. _sound = sound
  210. Return Self
  211. End Method
  212. ' - usually applied if this object was created with AllocChannel()
  213. Method Set(soloud:TSoloud, voiceHandle:Int, sound:TSLAudioSource)
  214. _soloud = soloud
  215. _voiceHandle = voiceHandle
  216. _sound = sound
  217. If preflags & APPLY_PAUSED Then
  218. SetPaused(prePaused)
  219. End If
  220. If preflags & APPLY_VOLUME Then
  221. SetVolume(preVolume)
  222. End If
  223. If preflags & APPLY_PAN Then
  224. SetPan(prePan)
  225. End If
  226. If preflags & APPLY_RATE Then
  227. SetRate(preRate)
  228. End If
  229. End Method
  230. Method Stop() Override
  231. If _voiceHandle Then
  232. _soloud.stop(_voiceHandle)
  233. End If
  234. End Method
  235. Method SetPaused( paused:Int ) Override
  236. If _voiceHandle Then
  237. _soloud.setPause(_voiceHandle, paused)
  238. Else
  239. preflags:| APPLY_PAUSED
  240. prePaused = paused
  241. End If
  242. End Method
  243. Method SetVolume( volume:Float ) Override
  244. If _voiceHandle Then
  245. _soloud.setVolume(_voiceHandle, volume)
  246. End If
  247. preflags:| APPLY_VOLUME
  248. preVolume = volume
  249. End Method
  250. Method SetPan( pan:Float ) Override
  251. If _voiceHandle Then
  252. _soloud.setPan(_voiceHandle, pan)
  253. End If
  254. preflags:| APPLY_PAN
  255. prePan = pan
  256. End Method
  257. Method SetDepth( depth:Float ) Override
  258. ' TODO ?
  259. End Method
  260. Method SetRate( rate:Float ) Override
  261. If _voiceHandle Then
  262. _soloud.setSamplerate(_voiceHandle, rate)
  263. End If
  264. preflags:| APPLY_RATE
  265. preRate = rate
  266. End Method
  267. Method Playing:Int() Override
  268. If _voiceHandle Then
  269. Return _soloud.isValidVoiceHandle(_voiceHandle) And Not _soloud.getPause(_voiceHandle)
  270. Else
  271. Return False
  272. End If
  273. End Method
  274. Method Position:Int()
  275. If _voiceHandle Then
  276. Return Int(_soloud.getStreamPosition(_voiceHandle) * 1000)
  277. Else
  278. Return 0
  279. End If
  280. End Method
  281. Method StreamTime:Int()
  282. If _voiceHandle Then
  283. Return Int(_soloud.getStreamTime(_voiceHandle) * 1000)
  284. Else
  285. Return 0
  286. End If
  287. End Method
  288. Method LoopCount:Int()
  289. If _voiceHandle Then
  290. Return _soloud.getLoopCount(_voiceHandle)
  291. Else
  292. Return 0
  293. End If
  294. End Method
  295. Method Length:Int()
  296. If _sound Then
  297. Return Int(_sound.getLength() * 1000)
  298. Else
  299. Return -1
  300. End If
  301. End Method
  302. Method SetProtected(protect:Int)
  303. If _voiceHandle Then
  304. _soloud.setProtectVoice(_voiceHandle, protect)
  305. End If
  306. End Method
  307. End Type
  308. Private
  309. Global audio_loaders:TAudioSourceLoader
  310. Public
  311. New TSoloudWavLoader(5)
  312. New TSoloudMonotoneLoader(10)
  313. New TSoloudAyLoader(15)
  314. Rem
  315. bbdoc:
  316. End Rem
  317. Type TAudioSourceLoader
  318. Field _succ:TAudioSourceLoader
  319. Field _priority:Int
  320. Method New(priority:Int)
  321. Self._priority = priority
  322. Local loader:TAudioSourceLoader = audio_loaders
  323. Local last:TAudioSourceLoader
  324. While loader
  325. If priority <= loader._priority Then
  326. _succ=audio_loaders
  327. audio_loaders=Self
  328. Return
  329. End If
  330. last = loader
  331. loader = loader._succ
  332. Wend
  333. If last Then
  334. If last._succ Then Throw "Unexpected audio loader entry"
  335. last._succ = Self
  336. Else
  337. audio_loaders = Self
  338. End If
  339. End Method
  340. Rem
  341. bbdoc: Load an audio sample
  342. returns: A new audio sample object, or Null if sample could not be loaded
  343. about: Extending types must implement this method.
  344. End Rem
  345. Method LoadAudioSource:TSLLoadableAudioSource( stream:TStream, flags:Int ) Abstract
  346. End Type
  347. Type TSoloudWavLoader Extends TAudioSourceLoader
  348. Method LoadAudioSource:TSLLoadableAudioSource( stream:TStream, flags:Int )
  349. Local sound:TSLLoadableAudioSource
  350. If flags & SOUND_STREAM Then
  351. sound = New TSLWavStream
  352. If sound.loadStream(stream) = SO_NO_ERROR Then
  353. Return sound
  354. End If
  355. sound.destroy()
  356. End If
  357. sound = New TSLWav
  358. If sound.loadStream(stream) = SO_NO_ERROR Then
  359. Return sound
  360. End If
  361. sound.destroy()
  362. End Method
  363. End Type
  364. Type TSoloudMonotoneLoader Extends TAudioSourceLoader
  365. Method LoadAudioSource:TSLLoadableAudioSource( stream:TStream, flags:Int )
  366. Local sound:TSLLoadableAudioSource = New TSLMonotone
  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 TSoloudAyLoader Extends TAudioSourceLoader
  374. Method LoadAudioSource:TSLLoadableAudioSource( stream:TStream, flags:Int )
  375. Local sound:TSLLoadableAudioSource = New TSLAy
  376. If sound.loadStream(stream) = SO_NO_ERROR Then
  377. Return sound
  378. End If
  379. sound.destroy()
  380. End Method
  381. End Type