diff --git a/exp/audio/audio.go b/exp/audio/audio.go index 07c4924ae..7c6f5414c 100644 --- a/exp/audio/audio.go +++ b/exp/audio/audio.go @@ -89,10 +89,9 @@ func (s *mixedPlayersStream) Read(b []byte) (int, error) { // TODO: Enable to specify the format like Mono8? type Context struct { - sampleRate int - stream *mixedPlayersStream - innerPlayer *player - players map[*Player]struct{} + sampleRate int + stream *mixedPlayersStream + players map[*Player]struct{} sync.Mutex } @@ -103,12 +102,9 @@ func NewContext(sampleRate int) *Context { players: map[*Player]struct{}{}, } c.stream = &mixedPlayersStream{c} - var err error - c.innerPlayer, err = newPlayer(c.stream, c.sampleRate) - if err != nil { + if err := startPlaying(c.stream, c.sampleRate); err != nil { panic(fmt.Sprintf("audio: NewContext error: %v", err)) } - c.innerPlayer.play() return c } diff --git a/exp/audio/audio_js.go b/exp/audio/audio_js.go index 78b537b8e..af94e38d1 100644 --- a/exp/audio/audio_js.go +++ b/exp/audio/audio_js.go @@ -32,6 +32,8 @@ type player struct { bufferSource *js.Object } +var currentPlayer *player + func initialize() bool { // Do nothing in node.js. if js.Global.Get("require") != js.Undefined { @@ -49,20 +51,26 @@ func initialize() bool { return true } -func newPlayer(src io.Reader, sampleRate int) (*player, error) { +func startPlaying(src io.Reader, sampleRate int) error { + if currentPlayer != nil { + panic("audio: currentPlayer already exists") + } if context == nil { if !initialize() { - panic("audio couldn't be initialized") + panic("audio: audio couldn't be initialized") } } - p := &player{ + currentPlayer = &player{ src: src, sampleRate: sampleRate, position: context.Get("currentTime").Float(), bufferSource: nil, } - return p, nil + if err := currentPlayer.start(); err != nil { + return err + } + return nil } func toLR(data []byte) ([]int16, []int16) { @@ -99,7 +107,7 @@ func (p *player) proceed() error { return err } -func (p *player) play() error { +func (p *player) start() error { // TODO: What if play is already called? go func() { defer p.close() diff --git a/exp/audio/audio_openal.go b/exp/audio/audio_openal.go index 4897846a0..83557d339 100644 --- a/exp/audio/audio_openal.go +++ b/exp/audio/audio_openal.go @@ -20,7 +20,6 @@ import ( "fmt" "io" "runtime" - "sync" "time" "golang.org/x/mobile/exp/audio/al" @@ -40,34 +39,35 @@ type player struct { isClosed bool } -var m sync.Mutex - -func newPlayer(src io.Reader, sampleRate int) (*player, error) { - m.Lock() - defer m.Unlock() +var currentPlayer *player +func startPlaying(src io.Reader, sampleRate int) error { + if currentPlayer != nil { + panic("audio: currentPlayer already exists") + } if e := al.OpenDevice(); e != nil { - m.Unlock() - return nil, fmt.Errorf("audio: OpenAL initialization failed: %v", e) + return fmt.Errorf("audio: OpenAL initialization failed: %v", e) } s := al.GenSources(1) if err := al.Error(); err != 0 { panic(fmt.Sprintf("audio: al.GenSources error: %d", err)) } - p := &player{ + currentPlayer = &player{ alSource: s[0], alBuffers: []al.Buffer{}, source: src, sampleRate: sampleRate, } - runtime.SetFinalizer(p, (*player).close) - return p, nil + runtime.SetFinalizer(currentPlayer, (*player).close) + if err := currentPlayer.start(); err != nil { + return err + } + return nil } const bufferSize = 1024 func (p *player) proceed() error { - m.Lock() if err := al.Error(); err != 0 { panic(fmt.Sprintf("audio: before proceed: %d", err)) } @@ -80,13 +80,11 @@ func (p *player) proceed() error { } p.alBuffers = append(p.alBuffers, bufs...) } - m.Unlock() for 0 < len(p.alBuffers) { b := make([]byte, bufferSize) n, err := p.source.Read(b) if 0 < n { - m.Lock() buf := p.alBuffers[0] p.alBuffers = p.alBuffers[1:] buf.BufferData(al.FormatStereo16, b[:n], int32(p.sampleRate)) @@ -94,14 +92,12 @@ func (p *player) proceed() error { if err := al.Error(); err != 0 { panic(fmt.Sprintf("audio: Queue in process: %d", err)) } - m.Unlock() } if err != nil { return err } } - m.Lock() if p.alSource.State() == al.Stopped { al.RewindSources(p.alSource) al.PlaySources(p.alSource) @@ -109,14 +105,11 @@ func (p *player) proceed() error { panic(fmt.Sprintf("audio: PlaySource in process: %d", err)) } } - m.Unlock() return nil } -func (p *player) play() error { - // TODO: What if play is already called? - m.Lock() +func (p *player) start() error { n := maxBufferNum - int(p.alSource.BuffersQueued()) - len(p.alBuffers) if 0 < n { p.alBuffers = append(p.alBuffers, al.GenBuffers(n)...) @@ -135,7 +128,6 @@ func (p *player) play() error { p.alBuffers = []al.Buffer{} } al.PlaySources(p.alSource) - m.Unlock() go func() { // TODO: Is it OK to close asap? @@ -155,10 +147,8 @@ func (p *player) play() error { return nil } +// TODO: When is this called? Can we remove this? func (p *player) close() error { - m.Lock() - defer m.Unlock() - if err := al.Error(); err != 0 { panic(fmt.Sprintf("audio: error before closing: %d", err)) }