player.go 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. // Copyright 2016 The G3N Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. //go:build !wasm
  5. // +build !wasm
  6. package audio
  7. // #include <stdlib.h>
  8. import "C"
  9. import (
  10. "io"
  11. "time"
  12. "unsafe"
  13. "github.com/g3n/engine/audio/al"
  14. "github.com/g3n/engine/core"
  15. "github.com/g3n/engine/gls"
  16. "github.com/g3n/engine/math32"
  17. )
  18. const (
  19. playerBufferCount = 2
  20. playerBufferSize = 32 * 1024
  21. )
  22. // Player is a 3D (spatial) audio file player
  23. // It embeds a core.Node so it can be inserted as a child in any other 3D object.
  24. type Player struct {
  25. core.Node // Embedded node
  26. af *AudioFile // Pointer to media audio file
  27. buffers []uint32 // OpenAL buffer names
  28. source uint32 // OpenAL source name
  29. nextBuf int // Index of next buffer to fill
  30. pdata unsafe.Pointer // Pointer to C allocated storage
  31. disposed bool // Disposed flag
  32. gchan chan (string) // Channel for informing of goroutine end
  33. }
  34. // NewPlayer creates and returns a pointer to a new audio player object
  35. // which will play the audio encoded in the specified file.
  36. // Currently it supports wave and Ogg Vorbis formats.
  37. func NewPlayer(filename string) (*Player, error) {
  38. // Try to open audio file
  39. af, err := NewAudioFile(filename)
  40. if err != nil {
  41. return nil, err
  42. }
  43. // Creates player
  44. p := new(Player)
  45. p.Node.Init(p)
  46. p.af = af
  47. // Generate buffers names
  48. p.buffers = al.GenBuffers(playerBufferCount)
  49. // Generate source name
  50. p.source = al.GenSource()
  51. // Allocates C memory buffer
  52. p.pdata = C.malloc(playerBufferSize)
  53. // Initialize channel for communication with internal goroutine
  54. p.gchan = make(chan string, 1)
  55. return p, nil
  56. }
  57. // Dispose disposes of this player resources
  58. func (p *Player) Dispose() {
  59. p.Stop()
  60. // Close file
  61. p.af.Close()
  62. // Release OpenAL resources
  63. al.DeleteSource(p.source)
  64. al.DeleteBuffers(p.buffers)
  65. // Release C memory
  66. C.free(p.pdata)
  67. p.pdata = nil
  68. p.disposed = true
  69. }
  70. // State returns the current state of this player
  71. func (p *Player) State() int {
  72. return int(al.GetSourcei(p.source, al.SourceState))
  73. }
  74. // Play starts playing this player
  75. func (p *Player) Play() error {
  76. state := p.State()
  77. // If paused, goroutine should be running, just starts playing
  78. if state == al.Paused {
  79. al.SourcePlay(p.source)
  80. return nil
  81. }
  82. // Already playing - stop in order to start from beginning
  83. if state == al.Playing {
  84. p.Stop()
  85. }
  86. // Sets file pointer to the beginning
  87. err := p.af.Seek(0)
  88. if err != nil {
  89. return err
  90. }
  91. // Fill buffers with decoded data
  92. for i := 0; i < playerBufferCount; i++ {
  93. err = p.fillBuffer(p.buffers[i])
  94. if err != nil {
  95. if err != io.EOF {
  96. return err
  97. }
  98. break
  99. }
  100. }
  101. p.nextBuf = 0
  102. // Clear previous goroutine response channel
  103. select {
  104. case _ = <-p.gchan:
  105. default:
  106. }
  107. // Starts playing and starts goroutine to fill buffers
  108. al.SourcePlay(p.source)
  109. go p.run()
  110. return nil
  111. }
  112. // Pause sets the player in the pause state
  113. func (p *Player) Pause() {
  114. if p.State() == al.Paused {
  115. return
  116. }
  117. al.SourcePause(p.source)
  118. }
  119. // Stop stops the player
  120. func (p *Player) Stop() {
  121. state := p.State()
  122. if state == al.Stopped || state == al.Initial {
  123. return
  124. }
  125. al.SourceStop(p.source)
  126. // Waits for goroutine to finish
  127. <-p.gchan
  128. }
  129. // CurrentTime returns the current time in seconds spent in the stream
  130. func (p *Player) CurrentTime() float64 {
  131. return p.af.CurrentTime()
  132. }
  133. // TotalTime returns the total time in seconds to play this stream
  134. func (p *Player) TotalTime() float64 {
  135. return p.af.info.TotalTime
  136. }
  137. // Gain returns the current gain (volume) of this player
  138. func (p *Player) Gain() float32 {
  139. return al.GetSourcef(p.source, al.Gain)
  140. }
  141. // SetGain sets the gain (volume) of this player
  142. func (p *Player) SetGain(gain float32) {
  143. al.Sourcef(p.source, al.Gain, gain)
  144. }
  145. // MinGain returns the current minimum gain of this player
  146. func (p *Player) MinGain() float32 {
  147. return al.GetSourcef(p.source, al.MinGain)
  148. }
  149. // SetMinGain sets the minimum gain (volume) of this player
  150. func (p *Player) SetMinGain(gain float32) {
  151. al.Sourcef(p.source, al.MinGain, gain)
  152. }
  153. // MaxGain returns the current maximum gain of this player
  154. func (p *Player) MaxGain() float32 {
  155. return al.GetSourcef(p.source, al.MaxGain)
  156. }
  157. // SetMaxGain sets the maximum gain (volume) of this player
  158. func (p *Player) SetMaxGain(gain float32) {
  159. al.Sourcef(p.source, al.MaxGain, gain)
  160. }
  161. // Pitch returns the current pitch factor of this player
  162. func (p *Player) Pitch() float32 {
  163. return al.GetSourcef(p.source, al.Pitch)
  164. }
  165. // SetPitch sets the pitch factor of this player
  166. func (p *Player) SetPitch(pitch float32) {
  167. al.Sourcef(p.source, al.Pitch, pitch)
  168. }
  169. // Looping returns the current looping state of this player
  170. func (p *Player) Looping() bool {
  171. return p.af.Looping()
  172. }
  173. // SetLooping sets the looping state of this player
  174. func (p *Player) SetLooping(looping bool) {
  175. p.af.SetLooping(looping)
  176. }
  177. // InnerCone returns the inner cone angle in degrees
  178. func (p *Player) InnerCone() float32 {
  179. return al.GetSourcef(p.source, al.ConeInnerAngle)
  180. }
  181. // SetInnerCone sets the inner cone angle in degrees
  182. func (p *Player) SetInnerCone(inner float32) {
  183. al.Sourcef(p.source, al.ConeInnerAngle, inner)
  184. }
  185. // OuterCone returns the outer cone angle in degrees
  186. func (p *Player) OuterCone() float32 {
  187. return al.GetSourcef(p.source, al.ConeOuterAngle)
  188. }
  189. // SetOuterCone sets the outer cone angle in degrees
  190. func (p *Player) SetOuterCone(outer float32) {
  191. al.Sourcef(p.source, al.ConeOuterAngle, outer)
  192. }
  193. // SetVelocity sets the velocity of this player
  194. // It is used to calculate Doppler effects
  195. func (p *Player) SetVelocity(vx, vy, vz float32) {
  196. al.Source3f(p.source, al.Velocity, vx, vy, vz)
  197. }
  198. // SetVelocityVec sets the velocity of this player from the specified vector
  199. // It is used to calculate Doppler effects
  200. func (p Player) SetVelocityVec(v *math32.Vector3) {
  201. al.Source3f(p.source, al.Velocity, v.X, v.Y, v.Z)
  202. }
  203. // Velocity returns this player velocity
  204. func (p *Player) Velocity() (float32, float32, float32) {
  205. return al.GetSource3f(p.source, al.Velocity)
  206. }
  207. // VelocityVec returns this player velocity vector
  208. func (p *Player) VelocityVec() math32.Vector3 {
  209. vx, vy, vz := al.GetSource3f(p.source, al.Velocity)
  210. return math32.Vector3{vx, vy, vz}
  211. }
  212. // SetRolloffFactor sets this player rolloff factor user to calculate
  213. // the gain attenuation by distance
  214. func (p *Player) SetRolloffFactor(rfactor float32) {
  215. al.Sourcef(p.source, al.RolloffFactor, rfactor)
  216. }
  217. // Render satisfies the INode interface.
  218. // It is called by renderer at every frame and is used to
  219. // update the audio source position and direction
  220. func (p *Player) Render(gl *gls.GLS) {
  221. // Sets the player source world position
  222. var wpos math32.Vector3
  223. p.WorldPosition(&wpos)
  224. al.Source3f(p.source, al.Position, wpos.X, wpos.Y, wpos.Z)
  225. // Sets the player source world direction
  226. var wdir math32.Vector3
  227. p.WorldDirection(&wdir)
  228. al.Source3f(p.source, al.Direction, wdir.X, wdir.Y, wdir.Z)
  229. }
  230. // Goroutine to fill PCM buffers with decoded data for OpenAL
  231. func (p *Player) run() {
  232. for {
  233. // Get current state of player source
  234. state := al.GetSourcei(p.source, al.SourceState)
  235. processed := al.GetSourcei(p.source, al.BuffersProcessed)
  236. queued := al.GetSourcei(p.source, al.BuffersQueued)
  237. //log.Debug("state:%x processed:%v queued:%v", state, processed, queued)
  238. // If stopped, unqueues all buffer before exiting
  239. if state == al.Stopped {
  240. if queued == 0 {
  241. break
  242. }
  243. // Unqueue buffers
  244. if processed > 0 {
  245. al.SourceUnqueueBuffers(p.source, uint32(processed), nil)
  246. }
  247. continue
  248. }
  249. // If no buffers processed, sleeps and try again
  250. if processed == 0 {
  251. time.Sleep(20 * time.Millisecond)
  252. continue
  253. }
  254. // Remove processed buffers from the queue
  255. al.SourceUnqueueBuffers(p.source, uint32(processed), nil)
  256. // Fill and enqueue buffers with new data
  257. for i := 0; i < int(processed); i++ {
  258. err := p.fillBuffer(p.buffers[p.nextBuf])
  259. if err != nil {
  260. break
  261. }
  262. p.nextBuf = (p.nextBuf + 1) % playerBufferCount
  263. }
  264. }
  265. // Sends indication of goroutine end
  266. p.gchan <- "end"
  267. }
  268. // fillBuffer fills the specified OpenAL buffer with next decoded data
  269. // and queues the buffer to this player source
  270. func (p *Player) fillBuffer(buf uint32) error {
  271. // Reads next decoded data
  272. n, err := p.af.Read(p.pdata, playerBufferSize)
  273. if err != nil {
  274. return err
  275. }
  276. // Sends data to buffer
  277. //log.Debug("BufferData:%v format:%x n:%v rate:%v", buf, p.af.info.Format, n, p.af.info.SampleRate)
  278. al.BufferData(buf, uint32(p.af.info.Format), p.pdata, uint32(n), uint32(p.af.info.SampleRate))
  279. al.SourceQueueBuffers(p.source, buf)
  280. return nil
  281. }