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()
}
theContextLock.Unlock()
if err != nil {
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

View File

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

View File

@ -16,31 +16,109 @@ package audio
import (
"io"
"io/ioutil"
"sync"
)
type (
dummyDriver struct{}
dummyPlayer struct{}
dummyWriterPlayerDriver struct{}
dummyWriterPlayer struct{}
)
func (d *dummyDriver) NewPlayer() io.WriteCloser {
return &dummyPlayer{}
func (d *dummyWriterPlayerDriver) NewPlayer() io.WriteCloser {
return &dummyWriterPlayer{}
}
func (d *dummyDriver) Close() error {
func (d *dummyWriterPlayerDriver) Close() error {
return nil
}
func (p *dummyPlayer) Write(b []byte) (int, error) {
func (p *dummyWriterPlayer) Write(b []byte) (int, error) {
return len(b), nil
}
func (p *dummyPlayer) Close() error {
func (p *dummyWriterPlayer) Close() error {
return nil
}
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 {
@ -78,6 +156,6 @@ func PlayersNumForTesting() int {
return n
}
func ResetContext() {
func ResetContextForTesting() {
theContext = nil
}

View File

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