audio.monkey2 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. #rem monkeydoc
  2. Highly experimental audio module!
  3. #end
  4. Namespace mojo.audio
  5. Private
  6. Function ALFormat:ALenum( format:AudioFormat )
  7. Local alFormat:ALenum
  8. Select format
  9. Case AudioFormat.Mono8
  10. alFormat=AL_FORMAT_MONO8
  11. Case AudioFormat.Mono16
  12. alFormat=AL_FORMAT_MONO16
  13. Case AudioFormat.Stereo8
  14. alFormat=AL_FORMAT_STEREO8
  15. Case AudioFormat.Stereo16
  16. alFormat=AL_FORMAT_STEREO16
  17. End
  18. Return alFormat
  19. End
  20. Public
  21. #rem monkeydoc Global instance of the AudioDevice class.
  22. #end
  23. Const Audio:=New AudioDevice
  24. #rem monkeydoc The AudioDevice class.
  25. #end
  26. Class AudioDevice
  27. '***** Internal *****
  28. #rem monkeydoc @hidden
  29. #end
  30. Method Init()
  31. Local error:=""
  32. _alcDevice=alcOpenDevice( Null )
  33. If _alcDevice
  34. _alcContext=alcCreateContext( _alcDevice,Null )
  35. If _alcContext
  36. If alcMakeContextCurrent( _alcContext )
  37. Return
  38. Else
  39. error="Failed to make OpenAL current"
  40. Endif
  41. Else
  42. error="Failed to create OpenAL context"
  43. Endif
  44. Else
  45. error="Failed to create OpenAL device"
  46. Endif
  47. End
  48. Private
  49. Field _alcDevice:ALCdevice Ptr
  50. Field _alcContext:ALCcontext Ptr
  51. Field _error:String
  52. Field _channels:=New Stack<Channel>
  53. Method FlushTmpChannels()
  54. Local put:=0
  55. For Local chan:=Eachin _channels
  56. If chan.Playing
  57. _channels[put]=chan;put+=1
  58. Continue
  59. Endif
  60. chan.Discard()
  61. Next
  62. _channels.Resize( put )
  63. End
  64. Method AllocTmpChannel:Channel()
  65. FlushTmpChannels()
  66. Local channel:=New Channel()
  67. _channels.Push( channel )
  68. Return channel
  69. End
  70. End
  71. #rem monkeydoc The Sound class.
  72. #end
  73. Class Sound
  74. #rem monkeydoc Creates a new sound.
  75. #end
  76. Method New( data:AudioData )
  77. alGenBuffers( 1,Varptr _alBuffer )
  78. alBufferData( _alBuffer,ALFormat( data.Format ),data.Data,data.Size,data.Hertz )
  79. End
  80. #rem monkeydoc Discards a sound.
  81. #end
  82. Method Discard()
  83. If Not _alBuffer Return
  84. alDeleteBuffers( 1,Varptr _alBuffer )
  85. _alBuffer=0
  86. End
  87. #rem monkeydoc Plays a sound through a temporary channel.
  88. The returned channel will be automatically discarded when it finishes playing or is stopped with [[Channel.Stop]].
  89. #end
  90. Method Play:Channel( loop:Bool=False )
  91. Local channel:=Audio.AllocTmpChannel()
  92. channel.Play( Self,loop )
  93. Return channel
  94. End
  95. #rem monkeydoc Loads a sound.
  96. #end
  97. Function Load:Sound( path:String )
  98. Local data:=AudioData.Load( path )
  99. If Not data Return Null
  100. Local sound:=New Sound( data )
  101. data.Discard()
  102. Return sound
  103. End
  104. Private
  105. Field _alBuffer:ALuint
  106. End
  107. Class Channel
  108. #rem monkeydoc Creates a new audio channel
  109. #end
  110. Method New()
  111. Audio.FlushTmpChannels()
  112. alGenSources( 1,Varptr _alSource )
  113. End
  114. #rem monkeydoc True if channel is playing audio.
  115. If the channel is playing audio but is in the paused state, this property will still return true.
  116. #end
  117. Property Playing:Bool()
  118. If Not _alSource Return False
  119. Local state:=ALState()
  120. Return state=AL_PLAYING Or state=AL_PAUSED
  121. End
  122. #rem monkeydoc True if channel is paused.
  123. #end
  124. Property Paused:Bool()
  125. If Not _alSource Return False
  126. Return ALState()=AL_PAUSED
  127. Setter( paused:Bool )
  128. If Not Playing Return
  129. If paused
  130. alSourcePause( _alSource )
  131. Else
  132. alSourcePlay( _alSource )
  133. Endif
  134. End
  135. #rem monkeydoc Channel volume in the range 0 to 1.
  136. #end
  137. Property Volume:Float()
  138. If Not _alSource Return 0
  139. Return _volume
  140. Setter( volume:Float )
  141. If Not _alSource Return
  142. _volume=Clamp( volume,0.0,1.0 )
  143. alSourcef( _alSource,AL_GAIN,_volume )
  144. End
  145. #rem monkeydoc Channel playback rate.
  146. #end
  147. Property Rate:Float()
  148. If Not _alSource Return 0
  149. Return _rate
  150. Setter( rate:Float )
  151. If Not _alSource Return
  152. _rate=rate
  153. alSourcef( _alSource,AL_PITCH,_rate )
  154. End
  155. #rem monkeydoc Channel pan in the range -1 (left) to 1 (right).
  156. #end
  157. Property Pan:Float()
  158. If Not _alSource Return 0
  159. Return _pan
  160. Setter( pan:Float)
  161. If Not _alSource Return
  162. _pan=Clamp( pan,-1.0,1.0 )
  163. Local x:=Sin( _pan ),z:=-Cos( _pan )
  164. alSource3f( _alSource,AL_POSITION,x,0,z )
  165. End
  166. #rem monkeydoc Discard channel resources.
  167. #end
  168. Method Discard()
  169. If Not _alSource Return
  170. alDeleteSources( 1,Varptr _alSource )
  171. _alSource=0
  172. End
  173. #rem monkeydoc Play a sound through the channel.
  174. #end
  175. Method Play( sound:Sound,loop:Bool=False )
  176. If Not _alSource Or Not sound Or Not sound._alBuffer Return
  177. alSourcei( _alSource,AL_LOOPING,loop ? AL_TRUE Else AL_FALSE )
  178. alSourcei( _alSource,AL_BUFFER,sound._alBuffer )
  179. alSourcePlay( _alSource )
  180. End
  181. #if __TARGET__<>"emscripten"
  182. #rem monkeydoc @hidden - Highly experimental!!!!!
  183. #end
  184. Method WaitQueued( queued:Int )
  185. While _queued>queued
  186. FlushProcessed()
  187. If _queued<=queued Return
  188. _waiting=True
  189. _future.Get()
  190. Wend
  191. End
  192. #rem monkeydoc @hidden - Highly experimental!!!!!
  193. #end
  194. Method Queue( data:AudioData )
  195. Local buf:ALuint
  196. If Not _tmpBuffers
  197. _tmpBuffers=New Stack<ALuint>
  198. _freeBuffers=New Stack<ALuint>
  199. _future=New Future<Int>
  200. _waiting=False
  201. _queued=0
  202. _timer=New Timer( 20,Lambda()
  203. FlushProcessed()
  204. End )
  205. Endif
  206. If _freeBuffers.Empty
  207. alGenBuffers( 1,Varptr buf )
  208. _tmpBuffers.Push( buf )
  209. Else
  210. buf=_freeBuffers.Pop()
  211. Endif
  212. alBufferData( buf,ALFormat( data.Format ),data.Data,data.Size,data.Hertz )
  213. alSourceQueueBuffers( _alSource,1,Varptr buf )
  214. _queued+=1
  215. Local state:=ALState()
  216. If state=AL_INITIAL Or state=AL_STOPPED alSourcePlay( _alSource )
  217. End
  218. #endif
  219. #rem monkeydoc Stops channel.
  220. #end
  221. Method Stop()
  222. If Not _alSource Return
  223. alSourceStop( _alSource )
  224. End
  225. Private
  226. Field _alSource:ALuint
  227. Field _volume:Float=1
  228. Field _rate:Float=1
  229. Field _pan:Float=0
  230. Field _tmpBuffers:Stack<ALuint>
  231. Field _freeBuffers:Stack<ALuint>
  232. #if __TARGET__<>"emscripten"
  233. Field _future:Future<Int>
  234. Field _waiting:Bool
  235. Field _queued:Int
  236. Field _timer:Timer
  237. Method FlushProcessed:Int()
  238. Local proc:ALint
  239. alGetSourcei( _alSource,AL_BUFFERS_PROCESSED,Varptr proc )
  240. ' Print "processed: "+proc
  241. If Not proc Return 0
  242. For Local i:=0 Until proc
  243. Local buf:ALuint
  244. alSourceUnqueueBuffers( _alSource,1,Varptr buf )
  245. _queued-=1
  246. If _tmpBuffers.Contains( buf ) _freeBuffers.Push( buf )
  247. Next
  248. If _waiting
  249. _waiting=False
  250. _future.Set( proc )
  251. Endif
  252. Return proc
  253. End
  254. #endif
  255. Method ALState:ALenum()
  256. Local state:ALenum
  257. alGetSourcei( _alSource,AL_SOURCE_STATE,Varptr state )
  258. Return state
  259. End
  260. End