audio: Change the spec: Now audio.Player's functions are always concurrent safe

This commit is contained in:
Hajime Hoshi 2017-06-04 02:52:11 +09:00
parent f53a55b63a
commit e7478b794a
4 changed files with 22 additions and 35 deletions

View File

@ -269,7 +269,6 @@ func (c *Context) Update() error {
} }
// SampleRate returns the sample rate. // SampleRate returns the sample rate.
// All audio source must have the same sample rate.
func (c *Context) SampleRate() int { func (c *Context) SampleRate() int {
return c.sampleRate return c.sampleRate
} }
@ -282,24 +281,17 @@ type ReadSeekCloser interface {
type bytesReadSeekCloser struct { type bytesReadSeekCloser struct {
reader *bytes.Reader reader *bytes.Reader
sync.Mutex
} }
func (b *bytesReadSeekCloser) Read(buf []uint8) (int, error) { func (b *bytesReadSeekCloser) Read(buf []uint8) (int, error) {
b.Lock()
defer b.Unlock()
return b.reader.Read(buf) return b.reader.Read(buf)
} }
func (b *bytesReadSeekCloser) Seek(offset int64, whence int) (int64, error) { func (b *bytesReadSeekCloser) Seek(offset int64, whence int) (int64, error) {
b.Lock()
defer b.Unlock()
return b.reader.Seek(offset, whence) return b.reader.Seek(offset, whence)
} }
func (b *bytesReadSeekCloser) Close() error { func (b *bytesReadSeekCloser) Close() error {
b.Lock()
defer b.Unlock()
b.reader = nil b.reader = nil
return nil return nil
} }
@ -312,10 +304,6 @@ func BytesReadSeekCloser(b []uint8) ReadSeekCloser {
} }
// Player is an audio player which has one stream. // Player is an audio player which has one stream.
//
// Player's functions are concurrent safe only if the underlying source is concurrent safe.
// For example, if you want to call Read and Seek in different goroutines,
// the underlying source given at NewPlayer must be conccurent safe.
type Player struct { type Player struct {
players *players players *players
src ReadSeekCloser src ReadSeekCloser
@ -381,11 +369,16 @@ func NewPlayerFromBytes(context *Context, src []byte) (*Player, error) {
// //
// When closing, the stream owned by the player will also be closed by calling its Close. // When closing, the stream owned by the player will also be closed by calling its Close.
// //
// Close is concurrent safe.
//
// Close returns error when closing the source returns error. // Close returns error when closing the source returns error.
func (p *Player) Close() error { func (p *Player) Close() error {
p.players.removePlayer(p) p.players.removePlayer(p)
runtime.SetFinalizer(p, nil) runtime.SetFinalizer(p, nil)
return p.src.Close() p.Lock()
err := p.src.Close()
p.Unlock()
return err
} }
func (p *Player) readToBuffer(length int) error { func (p *Player) readToBuffer(length int) error {
@ -427,18 +420,24 @@ func (p *Player) bufferLength() int {
// Play plays the stream. // Play plays the stream.
// //
// Play always returns nil. // Play always returns nil.
//
// Play is concurrent safe.
func (p *Player) Play() error { func (p *Player) Play() error {
p.players.addPlayer(p) p.players.addPlayer(p)
return nil return nil
} }
// IsPlaying returns boolean indicating whether the player is playing. // IsPlaying returns boolean indicating whether the player is playing.
//
// IsPlaying is concurrent safe.
func (p *Player) IsPlaying() bool { func (p *Player) IsPlaying() bool {
return p.players.hasPlayer(p) return p.players.hasPlayer(p)
} }
// Rewind rewinds the current position to the start. // Rewind rewinds the current position to the start.
// //
// Rewind is concurrent safe.
//
// Rewind returns error when seeking the source returns error. // Rewind returns error when seeking the source returns error.
func (p *Player) Rewind() error { func (p *Player) Rewind() error {
return p.Seek(0) return p.Seek(0)
@ -446,6 +445,8 @@ func (p *Player) Rewind() error {
// Seek seeks the position with the given offset. // Seek seeks the position with the given offset.
// //
// Seek is concurrent safe.
//
// Seek returns error when seeking the source returns error. // Seek returns error when seeking the source returns error.
func (p *Player) Seek(offset time.Duration) error { func (p *Player) Seek(offset time.Duration) error {
p.players.addSeeking(p) p.players.addSeeking(p)
@ -465,6 +466,8 @@ func (p *Player) Seek(offset time.Duration) error {
// Pause pauses the playing. // Pause pauses the playing.
// //
// Pause is concurrent safe.
//
// Pause always returns nil. // Pause always returns nil.
func (p *Player) Pause() error { func (p *Player) Pause() error {
p.players.removePlayer(p) p.players.removePlayer(p)
@ -472,6 +475,8 @@ func (p *Player) Pause() error {
} }
// Current returns the current position. // Current returns the current position.
//
// Current is concurrent safe.
func (p *Player) Current() time.Duration { func (p *Player) Current() time.Duration {
p.RLock() p.RLock()
defer p.RUnlock() defer p.RUnlock()
@ -480,6 +485,8 @@ func (p *Player) Current() time.Duration {
} }
// Volume returns the current volume of this player [0-1]. // Volume returns the current volume of this player [0-1].
//
// Volume is concurrent safe.
func (p *Player) Volume() float64 { func (p *Player) Volume() float64 {
p.RLock() p.RLock()
defer p.RUnlock() defer p.RUnlock()
@ -488,6 +495,8 @@ func (p *Player) Volume() float64 {
// SetVolume sets the volume of this player. // SetVolume sets the volume of this player.
// volume must be in between 0 and 1. This function panics otherwise. // volume must be in between 0 and 1. This function panics otherwise.
//
// SetVolume is concurrent safe.
func (p *Player) SetVolume(volume float64) { func (p *Player) SetVolume(volume float64) {
p.Lock() p.Lock()
defer p.Unlock() defer p.Unlock()

View File

@ -22,23 +22,17 @@ import (
"github.com/hajimehoshi/ebiten/audio" "github.com/hajimehoshi/ebiten/audio"
"github.com/hajimehoshi/ebiten/audio/internal/convert" "github.com/hajimehoshi/ebiten/audio/internal/convert"
"github.com/hajimehoshi/ebiten/internal/sync"
"github.com/jfreymuth/oggvorbis" "github.com/jfreymuth/oggvorbis"
) )
// Stream is a decoded audio stream. // Stream is a decoded audio stream.
//
// All Stream's functions are concurrent safe.
type Stream struct { type Stream struct {
decoded audio.ReadSeekCloser decoded audio.ReadSeekCloser
size int64 size int64
sync.Mutex
} }
// Read is implementation of io.Reader's Read. // Read is implementation of io.Reader's Read.
func (s *Stream) Read(p []byte) (int, error) { func (s *Stream) Read(p []byte) (int, error) {
s.Lock()
defer s.Unlock()
return s.decoded.Read(p) return s.decoded.Read(p)
} }
@ -46,15 +40,11 @@ func (s *Stream) Read(p []byte) (int, error) {
// //
// Note that Seek can take long since decoding is a relatively heavy task. // Note that Seek can take long since decoding is a relatively heavy task.
func (s *Stream) Seek(offset int64, whence int) (int64, error) { func (s *Stream) Seek(offset int64, whence int) (int64, error) {
s.Lock()
defer s.Unlock()
return s.decoded.Seek(offset, whence) return s.decoded.Seek(offset, whence)
} }
// Read is implementation of io.Closer's Close. // Read is implementation of io.Closer's Close.
func (s *Stream) Close() error { func (s *Stream) Close() error {
s.Lock()
defer s.Unlock()
return s.decoded.Close() return s.decoded.Close()
} }

View File

@ -22,22 +22,16 @@ import (
"github.com/hajimehoshi/ebiten/audio" "github.com/hajimehoshi/ebiten/audio"
"github.com/hajimehoshi/ebiten/audio/internal/convert" "github.com/hajimehoshi/ebiten/audio/internal/convert"
"github.com/hajimehoshi/ebiten/internal/sync"
) )
// Stream is a decoded audio stream. // Stream is a decoded audio stream.
//
// All Stream's functions are concurrent safe.
type Stream struct { type Stream struct {
inner audio.ReadSeekCloser inner audio.ReadSeekCloser
size int64 size int64
sync.Mutex
} }
// Read is implementation of io.Reader's Read. // Read is implementation of io.Reader's Read.
func (s *Stream) Read(p []byte) (int, error) { func (s *Stream) Read(p []byte) (int, error) {
s.Lock()
defer s.Unlock()
return s.inner.Read(p) return s.inner.Read(p)
} }
@ -45,15 +39,11 @@ func (s *Stream) Read(p []byte) (int, error) {
// //
// Note that Seek can take long since decoding is a relatively heavy task. // Note that Seek can take long since decoding is a relatively heavy task.
func (s *Stream) Seek(offset int64, whence int) (int64, error) { func (s *Stream) Seek(offset int64, whence int) (int64, error) {
s.Lock()
defer s.Unlock()
return s.inner.Seek(offset, whence) return s.inner.Seek(offset, whence)
} }
// Read is implementation of io.Closer's Close. // Read is implementation of io.Closer's Close.
func (s *Stream) Close() error { func (s *Stream) Close() error {
s.Lock()
defer s.Unlock()
return s.inner.Close() return s.inner.Close()
} }

View File

@ -120,8 +120,6 @@ func NewPlayer(audioContext *audio.Context) (*Player, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Decoded stream s is concurrent safe so that p will be concurrent safe.
// Read the documentation about audio package and its Player struct.
p, err := audio.NewPlayer(audioContext, s) p, err := audio.NewPlayer(audioContext, s)
if err != nil { if err != nil {
return nil, err return nil, err