mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-24 18:58:54 +01:00
audio: Change the spec: Now audio.Player's functions are always concurrent safe
This commit is contained in:
parent
f53a55b63a
commit
e7478b794a
@ -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()
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user