audio: Refactoring

This commit is contained in:
Hajime Hoshi 2021-08-21 19:52:11 +09:00
parent 5474a52392
commit fb1ab5e779
3 changed files with 59 additions and 78 deletions

View File

@ -56,7 +56,7 @@ const (
// //
// For a typical usage example, see examples/wav/main.go. // For a typical usage example, see examples/wav/main.go.
type Context struct { type Context struct {
np *readerPlayerFactory playerFactory *playerFactory
// inited represents whether the audio device is initialized and available or not. // inited represents whether the audio device is initialized and available or not.
// On Android, audio loop cannot be started unless JVM is accessible. After updating one frame, JVM should exist. // On Android, audio loop cannot be started unless JVM is accessible. After updating one frame, JVM should exist.
@ -68,7 +68,7 @@ type Context struct {
ready bool ready bool
readyOnce sync.Once readyOnce sync.Once
players map[playerImpl]struct{} players map[*player]struct{}
m sync.Mutex m sync.Mutex
semaphore chan struct{} semaphore chan struct{}
@ -98,23 +98,23 @@ func NewContext(sampleRate int) *Context {
} }
c := &Context{ c := &Context{
sampleRate: sampleRate, sampleRate: sampleRate,
np: newReaderPlayerFactory(sampleRate), playerFactory: newPlayerFactory(sampleRate),
players: map[playerImpl]struct{}{}, players: map[*player]struct{}{},
inited: make(chan struct{}), inited: make(chan struct{}),
semaphore: make(chan struct{}, 1), semaphore: make(chan struct{}, 1),
} }
theContext = c theContext = c
h := getHook() h := getHook()
h.OnSuspendAudio(func() error { h.OnSuspendAudio(func() error {
c.semaphore <- struct{}{} c.semaphore <- struct{}{}
c.np.suspend() c.playerFactory.suspend()
return nil return nil
}) })
h.OnResumeAudio(func() error { h.OnResumeAudio(func() error {
<-c.semaphore <-c.semaphore
c.np.resume() c.playerFactory.resume()
return nil return nil
}) })
@ -172,7 +172,7 @@ func (c *Context) setReady() {
c.m.Unlock() c.m.Unlock()
} }
func (c *Context) addPlayer(p playerImpl) { func (c *Context) addPlayer(p *player) {
c.m.Lock() c.m.Lock()
defer c.m.Unlock() defer c.m.Unlock()
c.players[p] = struct{}{} c.players[p] = struct{}{}
@ -188,7 +188,7 @@ func (c *Context) addPlayer(p playerImpl) {
} }
} }
func (c *Context) removePlayer(p playerImpl) { func (c *Context) removePlayer(p *player) {
c.m.Lock() c.m.Lock()
delete(c.players, p) delete(c.players, p)
c.m.Unlock() c.m.Unlock()
@ -200,17 +200,13 @@ func (c *Context) gcPlayers() error {
// Now reader players cannot call removePlayers from themselves in the current implementation. // Now reader players cannot call removePlayers from themselves in the current implementation.
// Underlying playering can be the pause state after fishing its playing, // Underlying playering can be the pause state after fishing its playing,
// but there is no way to notify this to readerPlayers so far. // but there is no way to notify this to players so far.
// Instead, let's check the states proactively every frame. // Instead, let's check the states proactively every frame.
for p := range c.players { for p := range c.players {
rp, ok := p.(*readerPlayer) if err := p.Err(); err != nil {
if !ok {
return nil
}
if err := rp.Err(); err != nil {
return err return err
} }
if !rp.IsPlaying() { if !p.IsPlaying() {
delete(c.players, p) delete(c.players, p)
} }
} }
@ -273,22 +269,7 @@ func (c *Context) waitUntilInited() {
// This means that if a Player plays an infinite stream, // This means that if a Player plays an infinite stream,
// the object is never GCed unless Close is called. // the object is never GCed unless Close is called.
type Player struct { type Player struct {
p playerImpl p *player
}
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. // NewPlayer creates a new player with the given stream.
@ -308,7 +289,7 @@ type playerImpl interface {
// A Player doesn't close src even if src implements io.Closer. // A Player doesn't close src even if src implements io.Closer.
// Closing the source is src owner's responsibility. // Closing the source is src owner's responsibility.
func (c *Context) NewPlayer(src io.Reader) (*Player, error) { func (c *Context) NewPlayer(src io.Reader) (*Player, error) {
pi, err := c.np.newPlayerImpl(c, src) pi, err := c.playerFactory.newPlayer(c, src)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -23,8 +23,8 @@ import (
) )
type ( type (
dummyReaderContext struct{} dummyContext struct{}
dummyReaderPlayer struct { dummyPlayer struct {
r io.Reader r io.Reader
playing bool playing bool
volume float64 volume float64
@ -32,32 +32,32 @@ type (
} }
) )
func (c *dummyReaderContext) NewPlayer(r io.Reader) readerdriver.Player { func (c *dummyContext) NewPlayer(r io.Reader) readerdriver.Player {
return &dummyReaderPlayer{ return &dummyPlayer{
r: r, r: r,
volume: 1, volume: 1,
} }
} }
func (c *dummyReaderContext) MaxBufferSize() int { func (c *dummyContext) MaxBufferSize() int {
return 48000 * channelNum * bitDepthInBytes / 4 return 48000 * channelNum * bitDepthInBytes / 4
} }
func (c *dummyReaderContext) Suspend() error { func (c *dummyContext) Suspend() error {
return nil return nil
} }
func (c *dummyReaderContext) Resume() error { func (c *dummyContext) Resume() error {
return nil return nil
} }
func (p *dummyReaderPlayer) Pause() { func (p *dummyPlayer) Pause() {
p.m.Lock() p.m.Lock()
p.playing = false p.playing = false
p.m.Unlock() p.m.Unlock()
} }
func (p *dummyReaderPlayer) Play() { func (p *dummyPlayer) Play() {
p.m.Lock() p.m.Lock()
p.playing = true p.playing = true
p.m.Unlock() p.m.Unlock()
@ -71,35 +71,35 @@ func (p *dummyReaderPlayer) Play() {
}() }()
} }
func (p *dummyReaderPlayer) IsPlaying() bool { func (p *dummyPlayer) IsPlaying() bool {
p.m.Lock() p.m.Lock()
defer p.m.Unlock() defer p.m.Unlock()
return p.playing return p.playing
} }
func (p *dummyReaderPlayer) Reset() { func (p *dummyPlayer) Reset() {
p.m.Lock() p.m.Lock()
defer p.m.Unlock() defer p.m.Unlock()
p.playing = false p.playing = false
} }
func (p *dummyReaderPlayer) Volume() float64 { func (p *dummyPlayer) Volume() float64 {
return p.volume return p.volume
} }
func (p *dummyReaderPlayer) SetVolume(volume float64) { func (p *dummyPlayer) SetVolume(volume float64) {
p.volume = volume p.volume = volume
} }
func (p *dummyReaderPlayer) UnplayedBufferSize() int { func (p *dummyPlayer) UnplayedBufferSize() int {
return 0 return 0
} }
func (p *dummyReaderPlayer) Err() error { func (p *dummyPlayer) Err() error {
return nil return nil
} }
func (p *dummyReaderPlayer) Close() error { func (p *dummyPlayer) Close() error {
p.m.Lock() p.m.Lock()
defer p.m.Unlock() defer p.m.Unlock()
p.playing = false p.playing = false
@ -107,7 +107,7 @@ func (p *dummyReaderPlayer) Close() error {
} }
func init() { func init() {
readerDriverForTesting = &dummyReaderContext{} driverForTesting = &dummyContext{}
} }
type dummyHook struct { type dummyHook struct {

View File

@ -23,49 +23,49 @@ import (
"github.com/hajimehoshi/ebiten/v2/audio/internal/readerdriver" "github.com/hajimehoshi/ebiten/v2/audio/internal/readerdriver"
) )
type readerPlayerFactory struct { type playerFactory struct {
context readerdriver.Context context readerdriver.Context
sampleRate int sampleRate int
m sync.Mutex m sync.Mutex
} }
var readerDriverForTesting readerdriver.Context var driverForTesting readerdriver.Context
func newReaderPlayerFactory(sampleRate int) *readerPlayerFactory { func newPlayerFactory(sampleRate int) *playerFactory {
f := &readerPlayerFactory{ f := &playerFactory{
sampleRate: sampleRate, sampleRate: sampleRate,
} }
if readerDriverForTesting != nil { if driverForTesting != nil {
f.context = readerDriverForTesting f.context = driverForTesting
} }
// TODO: Consider the hooks. // TODO: Consider the hooks.
return f return f
} }
type readerPlayer struct { type player struct {
context *Context context *Context
player readerdriver.Player player readerdriver.Player
src io.Reader src io.Reader
stream *timeStream stream *timeStream
factory *readerPlayerFactory factory *playerFactory
m sync.Mutex m sync.Mutex
} }
func (f *readerPlayerFactory) newPlayerImpl(context *Context, src io.Reader) (playerImpl, error) { func (f *playerFactory) newPlayer(context *Context, src io.Reader) (*player, error) {
f.m.Lock() f.m.Lock()
defer f.m.Unlock() defer f.m.Unlock()
p := &readerPlayer{ p := &player{
src: src, src: src,
context: context, context: context,
factory: f, factory: f,
} }
runtime.SetFinalizer(p, (*readerPlayer).Close) runtime.SetFinalizer(p, (*player).Close)
return p, nil return p, nil
} }
func (f *readerPlayerFactory) suspend() error { func (f *playerFactory) suspend() error {
f.m.Lock() f.m.Lock()
defer f.m.Unlock() defer f.m.Unlock()
@ -75,7 +75,7 @@ func (f *readerPlayerFactory) suspend() error {
return f.context.Suspend() return f.context.Suspend()
} }
func (f *readerPlayerFactory) resume() error { func (f *playerFactory) resume() error {
f.m.Lock() f.m.Lock()
defer f.m.Unlock() defer f.m.Unlock()
@ -85,7 +85,7 @@ func (f *readerPlayerFactory) resume() error {
return f.context.Resume() return f.context.Resume()
} }
func (f *readerPlayerFactory) initContextIfNeeded() (<-chan struct{}, error) { func (f *playerFactory) initContextIfNeeded() (<-chan struct{}, error) {
f.m.Lock() f.m.Lock()
defer f.m.Unlock() defer f.m.Unlock()
@ -101,7 +101,7 @@ func (f *readerPlayerFactory) initContextIfNeeded() (<-chan struct{}, error) {
return ready, nil return ready, nil
} }
func (p *readerPlayer) ensurePlayer() error { func (p *player) ensurePlayer() error {
// Initialize the underlying player lazily to enable calling NewContext in an 'init' function. // Initialize the underlying player lazily to enable calling NewContext in an 'init' function.
// Accessing the underlying player functions requires the environment to be already initialized, // Accessing the underlying player functions requires the environment to be already initialized,
// but if Ebiten is used for a shared library, the timing when init functions are called // but if Ebiten is used for a shared library, the timing when init functions are called
@ -131,7 +131,7 @@ func (p *readerPlayer) ensurePlayer() error {
return nil return nil
} }
func (p *readerPlayer) Play() { func (p *player) Play() {
p.m.Lock() p.m.Lock()
defer p.m.Unlock() defer p.m.Unlock()
@ -146,7 +146,7 @@ func (p *readerPlayer) Play() {
p.context.addPlayer(p) p.context.addPlayer(p)
} }
func (p *readerPlayer) Pause() { func (p *player) Pause() {
p.m.Lock() p.m.Lock()
defer p.m.Unlock() defer p.m.Unlock()
@ -161,7 +161,7 @@ func (p *readerPlayer) Pause() {
p.context.removePlayer(p) p.context.removePlayer(p)
} }
func (p *readerPlayer) IsPlaying() bool { func (p *player) IsPlaying() bool {
p.m.Lock() p.m.Lock()
defer p.m.Unlock() defer p.m.Unlock()
@ -171,7 +171,7 @@ func (p *readerPlayer) IsPlaying() bool {
return p.player.IsPlaying() return p.player.IsPlaying()
} }
func (p *readerPlayer) Volume() float64 { func (p *player) Volume() float64 {
p.m.Lock() p.m.Lock()
defer p.m.Unlock() defer p.m.Unlock()
@ -182,7 +182,7 @@ func (p *readerPlayer) Volume() float64 {
return p.player.Volume() return p.player.Volume()
} }
func (p *readerPlayer) SetVolume(volume float64) { func (p *player) SetVolume(volume float64) {
p.m.Lock() p.m.Lock()
defer p.m.Unlock() defer p.m.Unlock()
@ -193,7 +193,7 @@ func (p *readerPlayer) SetVolume(volume float64) {
p.player.SetVolume(volume) p.player.SetVolume(volume)
} }
func (p *readerPlayer) Close() error { func (p *player) Close() error {
p.m.Lock() p.m.Lock()
defer p.m.Unlock() defer p.m.Unlock()
runtime.SetFinalizer(p, nil) runtime.SetFinalizer(p, nil)
@ -208,7 +208,7 @@ func (p *readerPlayer) Close() error {
return nil return nil
} }
func (p *readerPlayer) Current() time.Duration { func (p *player) Current() time.Duration {
p.m.Lock() p.m.Lock()
defer p.m.Unlock() defer p.m.Unlock()
if err := p.ensurePlayer(); err != nil { if err := p.ensurePlayer(); err != nil {
@ -220,11 +220,11 @@ func (p *readerPlayer) Current() time.Duration {
return time.Duration(sample) * time.Second / time.Duration(p.factory.sampleRate) return time.Duration(sample) * time.Second / time.Duration(p.factory.sampleRate)
} }
func (p *readerPlayer) Rewind() error { func (p *player) Rewind() error {
return p.Seek(0) return p.Seek(0)
} }
func (p *readerPlayer) Seek(offset time.Duration) error { func (p *player) Seek(offset time.Duration) error {
p.m.Lock() p.m.Lock()
defer p.m.Unlock() defer p.m.Unlock()
@ -241,7 +241,7 @@ func (p *readerPlayer) Seek(offset time.Duration) error {
return p.stream.Seek(offset) return p.stream.Seek(offset)
} }
func (p *readerPlayer) Err() error { func (p *player) Err() error {
p.m.Lock() p.m.Lock()
defer p.m.Unlock() defer p.m.Unlock()
@ -251,7 +251,7 @@ func (p *readerPlayer) Err() error {
return p.player.Err() return p.player.Err()
} }
func (p *readerPlayer) source() io.Reader { func (p *player) source() io.Reader {
return p.src return p.src
} }