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.