audio: Refactoring

This commit is contained in:
Hajime Hoshi 2021-01-07 02:06:09 +09:00
parent e04e709a10
commit a6b3f32f3a
4 changed files with 44 additions and 30 deletions

View File

@ -48,6 +48,10 @@ const (
bytesPerSample = 2 * channelNum bytesPerSample = 2 * channelNum
) )
type newPlayerImpler interface {
newPlayerImpl(context *Context, src io.Reader) (playerImpl, error)
}
// A Context represents a current state of audio. // A Context represents a current state of audio.
// //
// At most one Context object can exist in one process. // At most one Context object can exist in one process.
@ -55,7 +59,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 {
wc writerContext np newPlayerImpler
// 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.
@ -97,7 +101,7 @@ func NewContext(sampleRate int) *Context {
c := &Context{ c := &Context{
sampleRate: sampleRate, sampleRate: sampleRate,
wc: newWriterContext(sampleRate), np: newWriterContext(sampleRate),
players: map[playerImpl]struct{}{}, players: map[playerImpl]struct{}{},
inited: make(chan struct{}), inited: make(chan struct{}),
semaphore: make(chan struct{}, 1), semaphore: make(chan struct{}, 1),
@ -268,7 +272,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 NewPlayer(context *Context, src io.Reader) (*Player, error) { func NewPlayer(context *Context, src io.Reader) (*Player, error) {
pi, err := newWriterContextPlayerImpl(context, context.wc, src) pi, err := context.np.newPlayerImpl(context, src)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -19,15 +19,15 @@ import (
) )
type ( type (
dummyContext struct{} dummyDriver struct{}
dummyPlayer struct{} dummyPlayer struct{}
) )
func (d *dummyContext) NewPlayer() io.WriteCloser { func (d *dummyDriver) NewPlayer() io.WriteCloser {
return &dummyPlayer{} return &dummyPlayer{}
} }
func (d *dummyContext) Close() error { func (d *dummyDriver) Close() error {
return nil return nil
} }
@ -40,7 +40,7 @@ func (p *dummyPlayer) Close() error {
} }
func init() { func init() {
writerContextForTesting = &dummyContext{} writerDriverForTesting = &dummyDriver{}
} }
type dummyHook struct { type dummyHook struct {

View File

@ -21,14 +21,14 @@ import (
"github.com/hajimehoshi/oto" "github.com/hajimehoshi/oto"
) )
func newOtoContext(sampleRate int, initCh chan struct{}) *otoContext { func newOtoDriver(sampleRate int, initCh chan struct{}) *otoDriver {
return &otoContext{ return &otoDriver{
sampleRate: sampleRate, sampleRate: sampleRate,
initCh: initCh, initCh: initCh,
} }
} }
type otoContext struct { type otoDriver struct {
sampleRate int sampleRate int
initCh <-chan struct{} initCh <-chan struct{}
@ -36,18 +36,18 @@ type otoContext struct {
once sync.Once once sync.Once
} }
func (c *otoContext) NewPlayer() io.WriteCloser { func (c *otoDriver) NewPlayer() io.WriteCloser {
return &otoPlayer{c: c} return &otoPlayer{c: c}
} }
func (c *otoContext) Close() error { func (c *otoDriver) Close() error {
if c.c == nil { if c.c == nil {
return nil return nil
} }
return c.c.Close() return c.c.Close()
} }
func (c *otoContext) ensureContext() error { func (c *otoDriver) ensureContext() error {
var err error var err error
c.once.Do(func() { c.once.Do(func() {
<-c.initCh <-c.initCh
@ -57,7 +57,7 @@ func (c *otoContext) ensureContext() error {
} }
type otoPlayer struct { type otoPlayer struct {
c *otoContext c *otoDriver
p *oto.Player p *oto.Player
once sync.Once once sync.Once
} }

View File

@ -23,18 +23,18 @@ import (
"github.com/hajimehoshi/ebiten/v2/internal/hooks" "github.com/hajimehoshi/ebiten/v2/internal/hooks"
) )
// writerContext represents a context represented as io.WriteClosers. // writerDriver represents a driver using io.WriteClosers.
// The actual implementation is oto.Context. // The actual implementation is otoDriver.
type writerContext interface { type writerDriver interface {
NewPlayer() io.WriteCloser NewPlayer() io.WriteCloser
io.Closer io.Closer
} }
var writerContextForTesting writerContext var writerDriverForTesting writerDriver
func newWriterContext(sampleRate int) writerContext { func newWriterDriver(sampleRate int) writerDriver {
if writerContextForTesting != nil { if writerDriverForTesting != nil {
return writerContextForTesting return writerDriverForTesting
} }
ch := make(chan struct{}) ch := make(chan struct{})
@ -45,12 +45,22 @@ func newWriterContext(sampleRate int) writerContext {
}) })
return nil return nil
}) })
return newOtoContext(sampleRate, ch) return newOtoDriver(sampleRate, ch)
}
type writerContext struct {
driver writerDriver
}
func newWriterContext(sampleRate int) *writerContext {
return &writerContext{
driver: newWriterDriver(sampleRate),
}
} }
type writerContextPlayerImpl struct { type writerContextPlayerImpl struct {
context *Context context *Context
writerContext writerContext driver writerDriver
src io.Reader src io.Reader
playing bool playing bool
closedExplicitly bool closedExplicitly bool
@ -64,12 +74,12 @@ type writerContextPlayerImpl struct {
m sync.Mutex m sync.Mutex
} }
func newWriterContextPlayerImpl(context *Context, writerContext writerContext, src io.Reader) (*writerContextPlayerImpl, error) { func (c *writerContext) newPlayerImpl(context *Context, src io.Reader) (playerImpl, error) {
p := &writerContextPlayerImpl{ p := &writerContextPlayerImpl{
context: context, context: context,
writerContext: writerContext, driver: c.driver,
src: src, src: src,
volume: 1, volume: 1,
} }
if seeker, ok := p.src.(io.Seeker); ok { if seeker, ok := p.src.(io.Seeker); ok {
// Get the current position of the source. // Get the current position of the source.
@ -119,7 +129,7 @@ func (p *writerContextPlayerImpl) Play() {
func (p *writerContextPlayerImpl) loop() { func (p *writerContextPlayerImpl) loop() {
p.context.waitUntilInited() p.context.waitUntilInited()
w := p.writerContext.NewPlayer() w := p.driver.NewPlayer()
wclosed := make(chan struct{}) wclosed := make(chan struct{})
defer func() { defer func() {
<-wclosed <-wclosed