mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-26 03:38:55 +01:00
audio: Refactoring
This commit is contained in:
parent
5474a52392
commit
fb1ab5e779
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user