audio.monkey2 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  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. End
  53. #rem monkeydoc The Sound class.
  54. #end
  55. Class Sound
  56. #rem monkeydoc Creates a new sound.
  57. #end
  58. Method New( data:AudioData )
  59. alGenBuffers( 1,Varptr _alBuffer )
  60. alBufferData( _alBuffer,ALFormat( data.Format ),data.Data,data.Size,data.Hertz )
  61. End
  62. #rem monkeydoc Discards a sound.
  63. #end
  64. Method Discard()
  65. If Not _alBuffer Return
  66. alDeleteBuffers( 1,Varptr _alBuffer )
  67. _alBuffer=0
  68. End
  69. #rem monkeydoc Plays a sound through a temporary channel.
  70. The returned channel will be automatically discarded when it finishes playing or is stopped with [[Channel.Stop]].
  71. #end
  72. Method Play:Channel( loop:Bool=False )
  73. Local channel:=New Channel( ChannelFlags.AutoDiscard )
  74. channel.Play( Self,loop )
  75. Return channel
  76. End
  77. #rem monkeydoc Loads a sound.
  78. #end
  79. Function Load:Sound( path:String )
  80. Local data:=AudioData.Load( path )
  81. If Not data Return Null
  82. Local sound:=New Sound( data )
  83. data.Discard()
  84. Return sound
  85. End
  86. Private
  87. Field _alBuffer:ALuint
  88. End
  89. #rem monkeydoc ChannelFlags enum.
  90. | Flag | Description
  91. |:--------------|:-----------
  92. | `AutoDiscard` | Channel will be automatically discarded when it finishes playing, or when it is stopped using [[Channel.Stop]].
  93. #end
  94. Enum ChannelFlags
  95. AutoDiscard=1
  96. End
  97. Class Channel
  98. #rem monkeydoc Creates a new audio channel.
  99. If `flags` is ChannelFlags.AutoDiscard, then the channel will be automatically discarded when it finishes playing, or when it is
  100. stopped using [[Stop]].
  101. #end
  102. Method New( flags:ChannelFlags=Null )
  103. _flags=flags
  104. FlushTmpChannels()
  105. alGenSources( 1,Varptr _alSource )
  106. If _flags & ChannelFlags.AutoDiscard _tmpChannels.Push( Self )
  107. _active+=1
  108. Print "Active channels="+_active
  109. End
  110. Property Flags:ChannelFlags()
  111. Return _flags
  112. End
  113. #rem monkeydoc True if channel is playing audio.
  114. If the channel is playing audio but is in the paused state, this property will still return true.
  115. #end
  116. Property Playing:Bool()
  117. If Not _alSource Return False
  118. Local state:=ALState()
  119. Return state=AL_PLAYING Or state=AL_PAUSED
  120. End
  121. #rem monkeydoc True if channel is paused.
  122. #end
  123. Property Paused:Bool()
  124. If Not _alSource Return False
  125. Return ALState()=AL_PAUSED
  126. Setter( paused:Bool )
  127. If Not Playing Return
  128. If paused
  129. alSourcePause( _alSource )
  130. Else
  131. alSourcePlay( _alSource )
  132. Endif
  133. End
  134. #rem monkeydoc Channel volume in the range 0 to 1.
  135. #end
  136. Property Volume:Float()
  137. If Not _alSource Return 0
  138. Return _volume
  139. Setter( volume:Float )
  140. If Not _alSource Return
  141. _volume=Clamp( volume,0.0,1.0 )
  142. alSourcef( _alSource,AL_GAIN,_volume )
  143. End
  144. #rem monkeydoc Channel playback rate.
  145. #end
  146. Property Rate:Float()
  147. If Not _alSource Return 0
  148. Return _rate
  149. Setter( rate:Float )
  150. If Not _alSource Return
  151. _rate=rate
  152. alSourcef( _alSource,AL_PITCH,_rate )
  153. End
  154. #rem monkeydoc Channel pan in the range -1 (left) to 1 (right).
  155. #end
  156. Property Pan:Float()
  157. If Not _alSource Return 0
  158. Return _pan
  159. Setter( pan:Float)
  160. If Not _alSource Return
  161. _pan=Clamp( pan,-1.0,1.0 )
  162. Local x:=Sin( _pan ),z:=-Cos( _pan )
  163. alSource3f( _alSource,AL_POSITION,x,0,z )
  164. End
  165. #rem monkeydoc Discard channel resources.
  166. #end
  167. Method Discard()
  168. If Not _alSource Return
  169. alDeleteSources( 1,Varptr _alSource )
  170. _alSource=0
  171. _active-=1
  172. Print "Active channels="+_active
  173. End
  174. #rem monkeydoc Play a sound through the channel.
  175. #end
  176. Method Play( sound:Sound,loop:Bool=False )
  177. If Not _alSource Or Not sound Or Not sound._alBuffer Return
  178. alSourcei( _alSource,AL_LOOPING,loop ? AL_TRUE Else AL_FALSE )
  179. alSourcei( _alSource,AL_BUFFER,sound._alBuffer )
  180. alSourcePlay( _alSource )
  181. End
  182. #if __TARGET__<>"emscripten"
  183. #rem monkeydoc @hidden - Highly experimental!!!!!
  184. #end
  185. Method WaitQueued( queued:Int )
  186. While _queued>queued
  187. FlushProcessed()
  188. If _queued<=queued Return
  189. _waiting=True
  190. _future.Get()
  191. Wend
  192. End
  193. #rem monkeydoc @hidden - Highly experimental!!!!!
  194. #end
  195. Method Queue( data:AudioData )
  196. Local buf:ALuint
  197. If Not _tmpBuffers
  198. _tmpBuffers=New Stack<ALuint>
  199. _freeBuffers=New Stack<ALuint>
  200. _future=New Future<Int>
  201. _waiting=False
  202. _queued=0
  203. _timer=New Timer( 60,Lambda()
  204. FlushProcessed()
  205. End )
  206. Endif
  207. If _freeBuffers.Empty
  208. alGenBuffers( 1,Varptr buf )
  209. _tmpBuffers.Push( buf )
  210. Else
  211. buf=_freeBuffers.Pop()
  212. Endif
  213. alBufferData( buf,ALFormat( data.Format ),data.Data,data.Size,data.Hertz )
  214. alSourceQueueBuffers( _alSource,1,Varptr buf )
  215. _queued+=1
  216. Local state:=ALState()
  217. If state=AL_INITIAL Or state=AL_STOPPED alSourcePlay( _alSource )
  218. End
  219. #endif
  220. #rem monkeydoc Stops channel.
  221. #end
  222. Method Stop()
  223. If Not _alSource Return
  224. alSourceStop( _alSource )
  225. If _flags & ChannelFlags.AutoDiscard Discard()
  226. End
  227. Private
  228. Field _flags:ChannelFlags
  229. Field _alSource:ALuint
  230. Field _volume:Float=1
  231. Field _rate:Float=1
  232. Field _pan:Float=0
  233. Global _active:=0
  234. Global _tmpChannels:=New Stack<Channel>
  235. Method ALState:ALenum()
  236. Local state:ALenum
  237. alGetSourcei( _alSource,AL_SOURCE_STATE,Varptr state )
  238. Return state
  239. End
  240. Function FlushTmpChannels()
  241. Local put:=0
  242. For Local chan:=Eachin _tmpChannels
  243. If Not chan._alSource Continue
  244. If chan.ALState()<>AL_STOPPED
  245. _tmpChannels[put]=chan;put+=1
  246. Continue
  247. Endif
  248. chan.Discard()
  249. Next
  250. _tmpChannels.Resize( put )
  251. End
  252. #if __TARGET__<>"emscripten"
  253. Field _tmpBuffers:Stack<ALuint>
  254. Field _freeBuffers:Stack<ALuint>
  255. Field _future:Future<Int>
  256. Field _waiting:Bool
  257. Field _queued:Int
  258. Field _timer:Timer
  259. Method FlushProcessed:Int()
  260. Local proc:ALint
  261. alGetSourcei( _alSource,AL_BUFFERS_PROCESSED,Varptr proc )
  262. ' Print "processed: "+proc
  263. If Not proc Return 0
  264. For Local i:=0 Until proc
  265. Local buf:ALuint
  266. alSourceUnqueueBuffers( _alSource,1,Varptr buf )
  267. _queued-=1
  268. If _tmpBuffers.Contains( buf ) _freeBuffers.Push( buf )
  269. Next
  270. If _waiting
  271. _waiting=False
  272. _future.Set( proc )
  273. Endif
  274. Return proc
  275. End
  276. #endif
  277. End