mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2025-01-23 17:32:02 +01:00
audio: Clarify concurrent safety
This commit is contained in:
parent
250afe97a5
commit
579491afbd
@ -282,27 +282,40 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
// BytesReadSeekCloser creates ReadSeekCloser from bytes.
|
// BytesReadSeekCloser creates ReadSeekCloser from bytes.
|
||||||
|
//
|
||||||
|
// A returned stream is concurrent safe.
|
||||||
func BytesReadSeekCloser(b []uint8) ReadSeekCloser {
|
func BytesReadSeekCloser(b []uint8) ReadSeekCloser {
|
||||||
return &bytesReadSeekCloser{bytes.NewReader(b)}
|
return &bytesReadSeekCloser{reader: bytes.NewReader(b)}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
|
@ -22,17 +22,23 @@ 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,11 +46,15 @@ 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()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,5 +205,5 @@ func Decode(context *audio.Context, src audio.ReadSeekCloser) (*Stream, error) {
|
|||||||
s = convert.NewResampling(s, size, sampleRate, context.SampleRate())
|
s = convert.NewResampling(s, size, sampleRate, context.SampleRate())
|
||||||
size = size * int64(context.SampleRate()) / int64(sampleRate)
|
size = size * int64(context.SampleRate()) / int64(sampleRate)
|
||||||
}
|
}
|
||||||
return &Stream{s, size}, nil
|
return &Stream{decoded: s, size: size}, nil
|
||||||
}
|
}
|
||||||
|
@ -22,16 +22,22 @@ 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,11 +45,15 @@ 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()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -222,5 +232,5 @@ chunks:
|
|||||||
s = convert.NewResampling(s, dataSize, sampleRateFrom, sampleRateTo)
|
s = convert.NewResampling(s, dataSize, sampleRateFrom, sampleRateTo)
|
||||||
dataSize = dataSize * int64(sampleRateTo) / int64(sampleRateFrom)
|
dataSize = dataSize * int64(sampleRateTo) / int64(sampleRateFrom)
|
||||||
}
|
}
|
||||||
return &Stream{s, dataSize}, nil
|
return &Stream{inner: s, size: dataSize}, nil
|
||||||
}
|
}
|
||||||
|
@ -116,6 +116,8 @@ 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