From e77beac2352d8624c4d09f306698ec32de116ad8 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Thu, 7 Jan 2021 00:59:13 +0900 Subject: [PATCH] audio: Define playerImpl interface --- audio/audio.go | 46 ++++++++++++++++++++++++------------------ audio/writercontext.go | 18 +++++++++++++++++ 2 files changed, 44 insertions(+), 20 deletions(-) diff --git a/audio/audio.go b/audio/audio.go index 977ac37fd..151f26215 100644 --- a/audio/audio.go +++ b/audio/audio.go @@ -66,7 +66,7 @@ type Context struct { err error ready bool - players map[*writerContextPlayerImpl]struct{} + players map[playerImpl]struct{} m sync.Mutex semaphore chan struct{} @@ -97,7 +97,7 @@ func NewContext(sampleRate int) *Context { c := &Context{ sampleRate: sampleRate, c: newWriterContext(sampleRate), - players: map[*writerContextPlayerImpl]struct{}{}, + players: map[playerImpl]struct{}{}, inited: make(chan struct{}), semaphore: make(chan struct{}, 1), } @@ -158,7 +158,7 @@ func (c *Context) setReady() { c.m.Unlock() } -func (c *Context) addPlayer(p *writerContextPlayerImpl) { +func (c *Context) addPlayer(p playerImpl) { c.m.Lock() defer c.m.Unlock() c.players[p] = struct{}{} @@ -174,7 +174,7 @@ func (c *Context) addPlayer(p *writerContextPlayerImpl) { } } -func (c *Context) removePlayer(p *writerContextPlayerImpl) { +func (c *Context) removePlayer(p playerImpl) { c.m.Lock() delete(c.players, p) c.m.Unlock() @@ -220,7 +220,22 @@ func (c *Context) SampleRate() int { // This means that if a Player plays an infinite stream, // the object is never GCed unless Close is called. type Player struct { - p *writerContextPlayerImpl + p playerImpl +} + +type playerImpl interface { + io.Closer + + Play() + IsPlaying() bool + Pause() + Volume() float64 + SetVolume(volume float64) + Current() time.Duration + Rewind() error + Seek(offset time.Duration) error + + source() io.Reader } // NewPlayer creates a new player with the given stream. @@ -240,22 +255,13 @@ type Player struct { // A Player doesn't close src even if src implements io.Closer. // Closing the source is src owner's responsibility. func NewPlayer(context *Context, src io.Reader) (*Player, error) { - p := &Player{ - &writerContextPlayerImpl{ - context: context, - src: src, - sampleRate: context.sampleRate, - volume: 1, - }, - } - if seeker, ok := p.p.src.(io.Seeker); ok { - // Get the current position of the source. - pos, err := seeker.Seek(0, io.SeekCurrent) - if err != nil { - return nil, err - } - p.p.pos = pos + pi, err := newWriterContextPlayerImpl(context, src) + if err != nil { + return nil, err } + + p := &Player{pi} + runtime.SetFinalizer(p, (*Player).finalize) return p, nil diff --git a/audio/writercontext.go b/audio/writercontext.go index 15b96db5b..5cf0e3bfe 100644 --- a/audio/writercontext.go +++ b/audio/writercontext.go @@ -64,6 +64,24 @@ type writerContextPlayerImpl struct { m sync.Mutex } +func newWriterContextPlayerImpl(context *Context, src io.Reader) (*writerContextPlayerImpl, error) { + p := &writerContextPlayerImpl{ + context: context, + src: src, + sampleRate: context.sampleRate, + volume: 1, + } + if seeker, ok := p.src.(io.Seeker); ok { + // Get the current position of the source. + pos, err := seeker.Seek(0, io.SeekCurrent) + if err != nil { + return nil, err + } + p.pos = pos + } + return p, nil +} + func (p *writerContextPlayerImpl) Close() error { p.m.Lock() defer p.m.Unlock()