audio: Bug fix: Test failures on browsers

This commit is contained in:
Hajime Hoshi 2021-03-22 02:57:52 +09:00
parent a17fea39a1
commit 07c10880f4
4 changed files with 122 additions and 20 deletions

View File

@ -144,7 +144,24 @@ func NewContext(sampleRate int) *Context {
theContext.m.Unlock() theContext.m.Unlock()
} }
theContextLock.Unlock() theContextLock.Unlock()
if err != nil {
return err return err
}
// Now reader players cannot call removePlayers from themselves in the current implementation.
// Underlying playering can be the pause state after fishing its playing,
// but there is no way to notify this to readerPlayers so far.
// Instead, let's check the states proactively every frame.
for p := range c.players {
rp, ok := p.(*readerPlayer)
if !ok {
break
}
if !rp.IsPlaying() {
delete(c.players, p)
}
}
return nil
}) })
return c return c

View File

@ -30,7 +30,7 @@ func setup() {
} }
func teardown() { func teardown() {
ResetContext() ResetContextForTesting()
context = nil context = nil
} }

View File

@ -16,31 +16,109 @@ package audio
import ( import (
"io" "io"
"io/ioutil"
"sync"
) )
type ( type (
dummyDriver struct{} dummyWriterPlayerDriver struct{}
dummyPlayer struct{} dummyWriterPlayer struct{}
) )
func (d *dummyDriver) NewPlayer() io.WriteCloser { func (d *dummyWriterPlayerDriver) NewPlayer() io.WriteCloser {
return &dummyPlayer{} return &dummyWriterPlayer{}
} }
func (d *dummyDriver) Close() error { func (d *dummyWriterPlayerDriver) Close() error {
return nil return nil
} }
func (p *dummyPlayer) Write(b []byte) (int, error) { func (p *dummyWriterPlayer) Write(b []byte) (int, error) {
return len(b), nil return len(b), nil
} }
func (p *dummyPlayer) Close() error { func (p *dummyWriterPlayer) Close() error {
return nil return nil
} }
func init() { func init() {
writerDriverForTesting = &dummyDriver{} writerDriverForTesting = &dummyWriterPlayerDriver{}
}
type (
dummyReaderPlayerDriver struct{}
dummyReaderPlayer struct {
r io.Reader
playing bool
volume float64
m sync.Mutex
}
)
func (d *dummyReaderPlayerDriver) NewPlayer(r io.Reader) readerDriverPlayer {
return &dummyReaderPlayer{
r: r,
volume: 1,
}
}
func (d *dummyReaderPlayerDriver) Close() error {
return nil
}
func (p *dummyReaderPlayer) Pause() {
p.m.Lock()
p.playing = false
p.m.Unlock()
}
func (p *dummyReaderPlayer) Play() {
p.m.Lock()
p.playing = true
p.m.Unlock()
go func() {
if _, err := ioutil.ReadAll(p.r); err != nil {
panic(err)
}
p.m.Lock()
p.playing = false
p.m.Unlock()
}()
}
func (p *dummyReaderPlayer) IsPlaying() bool {
p.m.Lock()
defer p.m.Unlock()
return p.playing
}
func (p *dummyReaderPlayer) Reset() {
p.m.Lock()
defer p.m.Unlock()
p.playing = false
}
func (p *dummyReaderPlayer) Volume() float64 {
return p.volume
}
func (p *dummyReaderPlayer) SetVolume(volume float64) {
p.volume = volume
}
func (p *dummyReaderPlayer) UnplayedBufferSize() int64 {
return 0
}
func (p *dummyReaderPlayer) Close() error {
p.m.Lock()
defer p.m.Unlock()
p.playing = false
return nil
}
func init() {
readerDriverForTesting = &dummyReaderPlayerDriver{}
} }
type dummyHook struct { type dummyHook struct {
@ -78,6 +156,6 @@ func PlayersNumForTesting() int {
return n return n
} }
func ResetContext() { func ResetContextForTesting() {
theContext = nil theContext = nil
} }

View File

@ -60,17 +60,23 @@ type readerPlayerFactory struct {
sampleRate int sampleRate int
} }
var readerDriverForTesting readerDriver
func newReaderPlayerFactory(sampleRate int) *readerPlayerFactory { func newReaderPlayerFactory(sampleRate int) *readerPlayerFactory {
return &readerPlayerFactory{ f := &readerPlayerFactory{
sampleRate: sampleRate, sampleRate: sampleRate,
} }
if readerDriverForTesting != nil {
f.driver = readerDriverForTesting
}
// TODO: Consider the hooks. // TODO: Consider the hooks.
return f
} }
type readerPlayer struct { type readerPlayer struct {
context *Context context *Context
player readerDriverPlayer player readerDriverPlayer
src *timeStream stream *timeStream
factory *readerPlayerFactory factory *readerPlayerFactory
m sync.Mutex m sync.Mutex
} }
@ -84,7 +90,7 @@ func (f *readerPlayerFactory) newPlayerImpl(context *Context, src io.Reader) (pl
p := &readerPlayer{ p := &readerPlayer{
context: context, context: context,
src: s, stream: s,
factory: f, factory: f,
} }
return p, nil return p, nil
@ -104,7 +110,7 @@ func (p *readerPlayer) ensurePlayer() error {
p.factory.driver = d p.factory.driver = d
} }
if p.player == nil { if p.player == nil {
p.player = p.factory.driver.NewPlayer(p.src) p.player = p.factory.driver.NewPlayer(p.stream)
} }
return nil return nil
} }
@ -131,7 +137,8 @@ func (p *readerPlayer) Pause() {
n := p.player.UnplayedBufferSize() n := p.player.UnplayedBufferSize()
p.player.Pause() p.player.Pause()
p.src.Unread(int(n)) p.stream.Unread(int(n))
p.context.removePlayer(p)
} }
func (p *readerPlayer) IsPlaying() bool { func (p *readerPlayer) IsPlaying() bool {
@ -171,8 +178,8 @@ func (p *readerPlayer) Close() error {
p.m.Lock() p.m.Lock()
defer p.m.Unlock() defer p.m.Unlock()
p.context.removePlayer(p)
if p.player != nil { if p.player != nil {
p.player.Pause()
return p.player.Close() return p.player.Close()
} }
return nil return nil
@ -186,7 +193,7 @@ func (p *readerPlayer) Current() time.Duration {
return 0 return 0
} }
sample := (p.src.Current() - p.player.UnplayedBufferSize()) / bytesPerSample sample := (p.stream.Current() - p.player.UnplayedBufferSize()) / bytesPerSample
return time.Duration(sample) * time.Second / time.Duration(p.factory.sampleRate) return time.Duration(sample) * time.Second / time.Duration(p.factory.sampleRate)
} }
@ -206,11 +213,11 @@ func (p *readerPlayer) Seek(offset time.Duration) error {
} }
p.player.Reset() p.player.Reset()
} }
return p.src.Seek(offset) return p.stream.Seek(offset)
} }
func (p *readerPlayer) source() io.Reader { func (p *readerPlayer) source() io.Reader {
return p.src return p.stream.r
} }
type timeStream struct { type timeStream struct {