audio: Refactoring

This commit is contained in:
Hajime Hoshi 2016-03-05 01:52:28 +09:00
parent 136c09cd33
commit 76386213bd
3 changed files with 31 additions and 37 deletions

View File

@ -89,10 +89,9 @@ func (s *mixedPlayersStream) Read(b []byte) (int, error) {
// TODO: Enable to specify the format like Mono8? // TODO: Enable to specify the format like Mono8?
type Context struct { type Context struct {
sampleRate int sampleRate int
stream *mixedPlayersStream stream *mixedPlayersStream
innerPlayer *player players map[*Player]struct{}
players map[*Player]struct{}
sync.Mutex sync.Mutex
} }
@ -103,12 +102,9 @@ func NewContext(sampleRate int) *Context {
players: map[*Player]struct{}{}, players: map[*Player]struct{}{},
} }
c.stream = &mixedPlayersStream{c} c.stream = &mixedPlayersStream{c}
var err error if err := startPlaying(c.stream, c.sampleRate); err != nil {
c.innerPlayer, err = newPlayer(c.stream, c.sampleRate)
if err != nil {
panic(fmt.Sprintf("audio: NewContext error: %v", err)) panic(fmt.Sprintf("audio: NewContext error: %v", err))
} }
c.innerPlayer.play()
return c return c
} }

View File

@ -32,6 +32,8 @@ type player struct {
bufferSource *js.Object bufferSource *js.Object
} }
var currentPlayer *player
func initialize() bool { func initialize() bool {
// Do nothing in node.js. // Do nothing in node.js.
if js.Global.Get("require") != js.Undefined { if js.Global.Get("require") != js.Undefined {
@ -49,20 +51,26 @@ func initialize() bool {
return true 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 context == nil {
if !initialize() { if !initialize() {
panic("audio couldn't be initialized") panic("audio: audio couldn't be initialized")
} }
} }
p := &player{ currentPlayer = &player{
src: src, src: src,
sampleRate: sampleRate, sampleRate: sampleRate,
position: context.Get("currentTime").Float(), position: context.Get("currentTime").Float(),
bufferSource: nil, bufferSource: nil,
} }
return p, nil if err := currentPlayer.start(); err != nil {
return err
}
return nil
} }
func toLR(data []byte) ([]int16, []int16) { func toLR(data []byte) ([]int16, []int16) {
@ -99,7 +107,7 @@ func (p *player) proceed() error {
return err return err
} }
func (p *player) play() error { func (p *player) start() error {
// TODO: What if play is already called? // TODO: What if play is already called?
go func() { go func() {
defer p.close() defer p.close()

View File

@ -20,7 +20,6 @@ import (
"fmt" "fmt"
"io" "io"
"runtime" "runtime"
"sync"
"time" "time"
"golang.org/x/mobile/exp/audio/al" "golang.org/x/mobile/exp/audio/al"
@ -40,34 +39,35 @@ type player struct {
isClosed bool isClosed bool
} }
var m sync.Mutex var currentPlayer *player
func newPlayer(src io.Reader, sampleRate int) (*player, error) {
m.Lock()
defer m.Unlock()
func startPlaying(src io.Reader, sampleRate int) error {
if currentPlayer != nil {
panic("audio: currentPlayer already exists")
}
if e := al.OpenDevice(); e != nil { if e := al.OpenDevice(); e != nil {
m.Unlock() return fmt.Errorf("audio: OpenAL initialization failed: %v", e)
return nil, fmt.Errorf("audio: OpenAL initialization failed: %v", e)
} }
s := al.GenSources(1) s := al.GenSources(1)
if err := al.Error(); err != 0 { if err := al.Error(); err != 0 {
panic(fmt.Sprintf("audio: al.GenSources error: %d", err)) panic(fmt.Sprintf("audio: al.GenSources error: %d", err))
} }
p := &player{ currentPlayer = &player{
alSource: s[0], alSource: s[0],
alBuffers: []al.Buffer{}, alBuffers: []al.Buffer{},
source: src, source: src,
sampleRate: sampleRate, sampleRate: sampleRate,
} }
runtime.SetFinalizer(p, (*player).close) runtime.SetFinalizer(currentPlayer, (*player).close)
return p, nil if err := currentPlayer.start(); err != nil {
return err
}
return nil
} }
const bufferSize = 1024 const bufferSize = 1024
func (p *player) proceed() error { func (p *player) proceed() error {
m.Lock()
if err := al.Error(); err != 0 { if err := al.Error(); err != 0 {
panic(fmt.Sprintf("audio: before proceed: %d", err)) panic(fmt.Sprintf("audio: before proceed: %d", err))
} }
@ -80,13 +80,11 @@ func (p *player) proceed() error {
} }
p.alBuffers = append(p.alBuffers, bufs...) p.alBuffers = append(p.alBuffers, bufs...)
} }
m.Unlock()
for 0 < len(p.alBuffers) { for 0 < len(p.alBuffers) {
b := make([]byte, bufferSize) b := make([]byte, bufferSize)
n, err := p.source.Read(b) n, err := p.source.Read(b)
if 0 < n { if 0 < n {
m.Lock()
buf := p.alBuffers[0] buf := p.alBuffers[0]
p.alBuffers = p.alBuffers[1:] p.alBuffers = p.alBuffers[1:]
buf.BufferData(al.FormatStereo16, b[:n], int32(p.sampleRate)) buf.BufferData(al.FormatStereo16, b[:n], int32(p.sampleRate))
@ -94,14 +92,12 @@ func (p *player) proceed() error {
if err := al.Error(); err != 0 { if err := al.Error(); err != 0 {
panic(fmt.Sprintf("audio: Queue in process: %d", err)) panic(fmt.Sprintf("audio: Queue in process: %d", err))
} }
m.Unlock()
} }
if err != nil { if err != nil {
return err return err
} }
} }
m.Lock()
if p.alSource.State() == al.Stopped { if p.alSource.State() == al.Stopped {
al.RewindSources(p.alSource) al.RewindSources(p.alSource)
al.PlaySources(p.alSource) al.PlaySources(p.alSource)
@ -109,14 +105,11 @@ func (p *player) proceed() error {
panic(fmt.Sprintf("audio: PlaySource in process: %d", err)) panic(fmt.Sprintf("audio: PlaySource in process: %d", err))
} }
} }
m.Unlock()
return nil return nil
} }
func (p *player) play() error { func (p *player) start() error {
// TODO: What if play is already called?
m.Lock()
n := maxBufferNum - int(p.alSource.BuffersQueued()) - len(p.alBuffers) n := maxBufferNum - int(p.alSource.BuffersQueued()) - len(p.alBuffers)
if 0 < n { if 0 < n {
p.alBuffers = append(p.alBuffers, al.GenBuffers(n)...) p.alBuffers = append(p.alBuffers, al.GenBuffers(n)...)
@ -135,7 +128,6 @@ func (p *player) play() error {
p.alBuffers = []al.Buffer{} p.alBuffers = []al.Buffer{}
} }
al.PlaySources(p.alSource) al.PlaySources(p.alSource)
m.Unlock()
go func() { go func() {
// TODO: Is it OK to close asap? // TODO: Is it OK to close asap?
@ -155,10 +147,8 @@ func (p *player) play() error {
return nil return nil
} }
// TODO: When is this called? Can we remove this?
func (p *player) close() error { func (p *player) close() error {
m.Lock()
defer m.Unlock()
if err := al.Error(); err != 0 { if err := al.Error(); err != 0 {
panic(fmt.Sprintf("audio: error before closing: %d", err)) panic(fmt.Sprintf("audio: error before closing: %d", err))
} }