github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/mobile/exp/audio/audio.go (about) 1 // Copyright 2015 The Go 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 5 // +build darwin linux 6 7 // Package audio provides a basic audio player. 8 // 9 // In order to use this package on Linux desktop distros, 10 // you will need OpenAL library as an external dependency. 11 // On Ubuntu 14.04 'Trusty', you may have to install this library 12 // by running the command below. 13 // 14 // sudo apt-get install libopenal-dev 15 // 16 // When compiled for Android, this package uses OpenAL Soft as a backend. 17 // Please add its license file to the open source notices of your 18 // application. 19 // OpenAL Soft's license file could be found at 20 // http://repo.or.cz/w/openal-soft.git/blob/HEAD:/COPYING. 21 package audio // import "golang.org/x/mobile/exp/audio" 22 23 import ( 24 "bytes" 25 "errors" 26 "fmt" 27 "io" 28 "sync" 29 "time" 30 31 "golang.org/x/mobile/exp/audio/al" 32 ) 33 34 // ReadSeekCloser is an io.ReadSeeker and io.Closer. 35 type ReadSeekCloser interface { 36 io.ReadSeeker 37 io.Closer 38 } 39 40 // Format represents a PCM data format. 41 type Format int 42 43 const ( 44 Mono8 Format = iota + 1 45 Mono16 46 Stereo8 47 Stereo16 48 ) 49 50 func (f Format) String() string { return formatStrings[f] } 51 52 // formatBytes is the product of bytes per sample and number of channels. 53 var formatBytes = [...]int64{ 54 Mono8: 1, 55 Mono16: 2, 56 Stereo8: 2, 57 Stereo16: 4, 58 } 59 60 var formatCodes = [...]uint32{ 61 Mono8: al.FormatMono8, 62 Mono16: al.FormatMono16, 63 Stereo8: al.FormatStereo8, 64 Stereo16: al.FormatStereo16, 65 } 66 67 var formatStrings = [...]string{ 68 0: "unknown", 69 Mono8: "mono8", 70 Mono16: "mono16", 71 Stereo8: "stereo8", 72 Stereo16: "stereo16", 73 } 74 75 // State indicates the current playing state of the player. 76 type State int 77 78 const ( 79 Unknown State = iota 80 Initial 81 Playing 82 Paused 83 Stopped 84 ) 85 86 func (s State) String() string { return stateStrings[s] } 87 88 var stateStrings = [...]string{ 89 Unknown: "unknown", 90 Initial: "initial", 91 Playing: "playing", 92 Paused: "paused", 93 Stopped: "stopped", 94 } 95 96 var codeToState = map[int32]State{ 97 0: Unknown, 98 al.Initial: Initial, 99 al.Playing: Playing, 100 al.Paused: Paused, 101 al.Stopped: Stopped, 102 } 103 104 type track struct { 105 format Format 106 samplesPerSecond int64 107 src ReadSeekCloser 108 109 // hasHeader represents whether the audio source contains 110 // a PCM header. If true, the audio data starts 44 bytes 111 // later in the source. 112 hasHeader bool 113 } 114 115 // Player is a basic audio player that plays PCM data. 116 // Operations on a nil *Player are no-op, a nil *Player can 117 // be used for testing purposes. 118 type Player struct { 119 t *track 120 source al.Source 121 122 mu sync.Mutex 123 prep bool 124 bufs []al.Buffer // buffers are created and queued to source during prepare. 125 sizeBytes int64 // size of the audio source 126 } 127 128 // NewPlayer returns a new Player. 129 // It initializes the underlying audio devices and the related resources. 130 // If zero values are provided for format and sample rate values, the player 131 // determines them from the source's WAV header. 132 // An error is returned if the format and sample rate can't be determined. 133 // 134 // The audio package is only designed for small audio sources. 135 func NewPlayer(src ReadSeekCloser, format Format, samplesPerSecond int64) (*Player, error) { 136 if err := al.OpenDevice(); err != nil { 137 return nil, err 138 } 139 s := al.GenSources(1) 140 if code := al.Error(); code != 0 { 141 return nil, fmt.Errorf("audio: cannot generate an audio source [err=%x]", code) 142 } 143 p := &Player{ 144 t: &track{format: format, src: src, samplesPerSecond: samplesPerSecond}, 145 source: s[0], 146 } 147 if err := p.discoverHeader(); err != nil { 148 return nil, err 149 } 150 if p.t.format == 0 { 151 return nil, errors.New("audio: cannot determine the format") 152 } 153 if p.t.samplesPerSecond == 0 { 154 return nil, errors.New("audio: cannot determine the sample rate") 155 } 156 return p, nil 157 } 158 159 // headerSize is the size of WAV headers. 160 // See http://www.topherlee.com/software/pcm-tut-wavformat.html. 161 const headerSize = 44 162 163 var ( 164 riffHeader = []byte("RIFF") 165 waveHeader = []byte("WAVE") 166 ) 167 168 func (p *Player) discoverHeader() error { 169 buf := make([]byte, headerSize) 170 if n, _ := io.ReadFull(p.t.src, buf); n != headerSize { 171 // No header present or read error. 172 return nil 173 } 174 if !(bytes.Equal(buf[0:4], riffHeader) && bytes.Equal(buf[8:12], waveHeader)) { 175 return nil 176 } 177 p.t.hasHeader = true 178 var format Format 179 switch channels, depth := buf[22], buf[34]; { 180 case channels == 1 && depth == 8: 181 format = Mono8 182 case channels == 1 && depth == 16: 183 format = Mono16 184 case channels == 2 && depth == 8: 185 format = Stereo8 186 case channels == 2 && depth == 16: 187 format = Stereo16 188 default: 189 return fmt.Errorf("audio: unsupported format; num of channels=%d, bit rate=%d", channels, depth) 190 } 191 if p.t.format == 0 { 192 p.t.format = format 193 } 194 if p.t.format != format { 195 return fmt.Errorf("audio: given format %v does not match header %v", p.t.format, format) 196 } 197 sampleRate := int64(buf[24]) | int64(buf[25])<<8 | int64(buf[26])<<16 | int64(buf[27]<<24) 198 if p.t.samplesPerSecond == 0 { 199 p.t.samplesPerSecond = sampleRate 200 } 201 if p.t.samplesPerSecond != sampleRate { 202 return fmt.Errorf("audio: given sample rate %v does not match header", p.t.samplesPerSecond, sampleRate) 203 } 204 return nil 205 } 206 207 func (p *Player) prepare(offset int64, force bool) error { 208 p.mu.Lock() 209 if !force && p.prep { 210 p.mu.Unlock() 211 return nil 212 } 213 p.mu.Unlock() 214 215 if p.t.hasHeader { 216 offset += headerSize 217 } 218 if _, err := p.t.src.Seek(offset, 0); err != nil { 219 return err 220 } 221 var bufs []al.Buffer 222 // TODO(jbd): Limit the number of buffers in use, unqueue and reuse 223 // the existing buffers as buffers are processed. 224 buf := make([]byte, 128*1024) 225 size := offset 226 for { 227 n, err := p.t.src.Read(buf) 228 if n > 0 { 229 size += int64(n) 230 b := al.GenBuffers(1) 231 b[0].BufferData(formatCodes[p.t.format], buf[:n], int32(p.t.samplesPerSecond)) 232 bufs = append(bufs, b[0]) 233 } 234 if err == io.EOF { 235 break 236 } 237 if err != nil { 238 return err 239 } 240 } 241 242 p.mu.Lock() 243 if len(p.bufs) > 0 { 244 p.source.UnqueueBuffers(p.bufs...) 245 al.DeleteBuffers(p.bufs...) 246 } 247 p.sizeBytes = size 248 p.bufs = bufs 249 p.prep = true 250 if len(bufs) > 0 { 251 p.source.QueueBuffers(bufs...) 252 } 253 p.mu.Unlock() 254 return nil 255 } 256 257 // Play buffers the source audio to the audio device and starts 258 // to play the source. 259 // If the player paused or stopped, it reuses the previously buffered 260 // resources to keep playing from the time it has paused or stopped. 261 func (p *Player) Play() error { 262 if p == nil { 263 return nil 264 } 265 // Prepares if the track hasn't been buffered before. 266 if err := p.prepare(0, false); err != nil { 267 return err 268 } 269 al.PlaySources(p.source) 270 return lastErr() 271 } 272 273 // Pause pauses the player. 274 func (p *Player) Pause() error { 275 if p == nil { 276 return nil 277 } 278 al.PauseSources(p.source) 279 return lastErr() 280 } 281 282 // Stop stops the player. 283 func (p *Player) Stop() error { 284 if p == nil { 285 return nil 286 } 287 al.StopSources(p.source) 288 return lastErr() 289 } 290 291 // Seek moves the play head to the given offset relative to the start of the source. 292 func (p *Player) Seek(offset time.Duration) error { 293 if p == nil { 294 return nil 295 } 296 if err := p.Stop(); err != nil { 297 return err 298 } 299 size := durToByteOffset(p.t, offset) 300 if err := p.prepare(size, true); err != nil { 301 return err 302 } 303 al.PlaySources(p.source) 304 return lastErr() 305 } 306 307 // Current returns the current playback position of the audio that is being played. 308 func (p *Player) Current() time.Duration { 309 if p == nil { 310 return 0 311 } 312 // TODO(jbd): Current never returns the Total when the playing is finished. 313 // OpenAL may be returning the last buffer's start point as an OffsetByte. 314 return byteOffsetToDur(p.t, int64(p.source.OffsetByte())) 315 } 316 317 // Total returns the total duration of the audio source. 318 func (p *Player) Total() time.Duration { 319 if p == nil { 320 return 0 321 } 322 // Prepare is required to determine the length of the source. 323 // We need to read the entire source to calculate the length. 324 p.prepare(0, false) 325 return byteOffsetToDur(p.t, p.sizeBytes) 326 } 327 328 // Volume returns the current player volume. The range of the volume is [0, 1]. 329 func (p *Player) Volume() float64 { 330 if p == nil { 331 return 0 332 } 333 return float64(p.source.Gain()) 334 } 335 336 // SetVolume sets the volume of the player. The range of the volume is [0, 1]. 337 func (p *Player) SetVolume(vol float64) { 338 if p == nil { 339 return 340 } 341 p.source.SetGain(float32(vol)) 342 } 343 344 // State returns the player's current state. 345 func (p *Player) State() State { 346 if p == nil { 347 return Unknown 348 } 349 return codeToState[p.source.State()] 350 } 351 352 // Close closes the device and frees the underlying resources 353 // used by the player. 354 // It should be called as soon as the player is not in-use anymore. 355 func (p *Player) Close() error { 356 if p == nil { 357 return nil 358 } 359 if p.source != 0 { 360 al.DeleteSources(p.source) 361 } 362 p.mu.Lock() 363 if len(p.bufs) > 0 { 364 al.DeleteBuffers(p.bufs...) 365 } 366 p.mu.Unlock() 367 p.t.src.Close() 368 return nil 369 } 370 371 func byteOffsetToDur(t *track, offset int64) time.Duration { 372 return time.Duration(offset * formatBytes[t.format] * int64(time.Second) / t.samplesPerSecond) 373 } 374 375 func durToByteOffset(t *track, dur time.Duration) int64 { 376 return int64(dur) * t.samplesPerSecond / (formatBytes[t.format] * int64(time.Second)) 377 } 378 379 // lastErr returns the last error or nil if the last operation 380 // has been succesful. 381 func lastErr() error { 382 if code := al.Error(); code != 0 { 383 return fmt.Errorf("audio: openal failed with %x", code) 384 } 385 return nil 386 } 387 388 // TODO(jbd): Close the device.