audio/internal/readerdriver: Refactoring

Updates #1549
This commit is contained in:
Hajime Hoshi 2021-06-06 15:17:02 +09:00
parent 048a30b4e7
commit a74c00074e
2 changed files with 62 additions and 49 deletions

View File

@ -49,9 +49,7 @@ type context struct {
context *C.pa_context context *C.pa_context
stream *C.pa_stream stream *C.pa_stream
players map[*playerImpl]struct{} players *players
buf []float32
cond *sync.Cond
} }
var theContext *context var theContext *context
@ -66,7 +64,9 @@ func NewContext(sampleRate, channelNum, bitDepthInBytes int) (Context, chan stru
sampleRate: sampleRate, sampleRate: sampleRate,
channelNum: channelNum, channelNum: channelNum,
bitDepthInBytes: bitDepthInBytes, bitDepthInBytes: bitDepthInBytes,
players: &players{
cond: sync.NewCond(&sync.Mutex{}), cond: sync.NewCond(&sync.Mutex{}),
},
} }
theContext = c theContext = c
@ -160,13 +160,19 @@ func NewContext(sampleRate, channelNum, bitDepthInBytes int) (Context, chan stru
C.pa_stream_cork(c.stream, 0, C.pa_stream_success_cb_t(C.ebiten_readerdriver_streamSuccessCallback), unsafe.Pointer(c.mainloop)) C.pa_stream_cork(c.stream, 0, C.pa_stream_success_cb_t(C.ebiten_readerdriver_streamSuccessCallback), unsafe.Pointer(c.mainloop))
go c.loop() go c.players.loop()
return c, ready, nil return c, ready, nil
} }
func (c *context) shouldWait() bool { type players struct {
for p := range c.players { players map[*playerImpl]struct{}
buf []float32
cond *sync.Cond
}
func (ps *players) shouldWait() bool {
for p := range ps.players {
if !p.isBufferFull() { if !p.isBufferFull() {
return false return false
} }
@ -174,26 +180,26 @@ func (c *context) shouldWait() bool {
return true return true
} }
func (c *context) wait() { func (ps *players) wait() {
c.cond.L.Lock() ps.cond.L.Lock()
defer c.cond.L.Unlock() defer ps.cond.L.Unlock()
for c.shouldWait() { for ps.shouldWait() {
c.cond.Wait() ps.cond.Wait()
} }
} }
func (c *context) loop() { func (ps *players) loop() {
var players []*playerImpl var players []*playerImpl
for { for {
c.wait() ps.wait()
c.cond.L.Lock() ps.cond.L.Lock()
players = players[:0] players = players[:0]
for p := range c.players { for p := range ps.players {
players = append(players, p) players = append(players, p)
} }
c.cond.L.Unlock() ps.cond.L.Unlock()
for _, p := range players { for _, p := range players {
p.readSourceToBuffer() p.readSourceToBuffer()
@ -211,22 +217,37 @@ func (c *context) Resume() error {
return nil return nil
} }
func (c *context) addPlayer(player *playerImpl) { func (ps *players) addPlayer(player *playerImpl) {
c.cond.L.Lock() ps.cond.L.Lock()
defer c.cond.L.Unlock() defer ps.cond.L.Unlock()
if c.players == nil { if ps.players == nil {
c.players = map[*playerImpl]struct{}{} ps.players = map[*playerImpl]struct{}{}
} }
c.players[player] = struct{}{} ps.players[player] = struct{}{}
c.cond.Signal() ps.cond.Signal()
} }
func (c *context) removePlayer(player *playerImpl) { func (ps *players) removePlayer(player *playerImpl) {
c.cond.L.Lock() ps.cond.L.Lock()
defer c.cond.L.Unlock() defer ps.cond.L.Unlock()
delete(c.players, player)
c.cond.Signal() delete(ps.players, player)
ps.cond.Signal()
}
func (ps *players) read(buf []float32) {
ps.cond.L.Lock()
players := make([]*playerImpl, 0, len(ps.players))
for p := range ps.players {
players = append(players, p)
}
ps.cond.L.Unlock()
for _, p := range players {
p.readBufferAndAdd(buf)
}
ps.cond.Signal()
} }
//export ebiten_readerdriver_contextStateCallback //export ebiten_readerdriver_contextStateCallback
@ -250,15 +271,7 @@ func ebiten_readerdriver_streamWriteCallback(stream *C.pa_stream, requestedBytes
var buf unsafe.Pointer var buf unsafe.Pointer
var buf32 []float32 var buf32 []float32
var bytesToFill C.size_t = bufferSize var bytesToFill C.size_t = bufferSize
var players []*playerImpl
for n := int(requestedBytes); n > 0; n -= int(bytesToFill) { for n := int(requestedBytes); n > 0; n -= int(bytesToFill) {
c.cond.L.Lock()
players = players[:0]
for p := range c.players {
players = append(players, p)
}
c.cond.L.Unlock()
C.pa_stream_begin_write(stream, &buf, &bytesToFill) C.pa_stream_begin_write(stream, &buf, &bytesToFill)
if len(buf32) < int(bytesToFill)/4 { if len(buf32) < int(bytesToFill)/4 {
buf32 = make([]float32, bytesToFill/4) buf32 = make([]float32, bytesToFill/4)
@ -267,10 +280,9 @@ func ebiten_readerdriver_streamWriteCallback(stream *C.pa_stream, requestedBytes
buf32[i] = 0 buf32[i] = 0
} }
} }
for _, p := range players {
p.readBufferAndAdd(buf32[:bytesToFill/4]) c.players.read(buf32[:bytesToFill/4])
}
c.cond.Signal()
for i := uintptr(0); i < uintptr(bytesToFill/4); i++ { for i := uintptr(0); i < uintptr(bytesToFill/4); i++ {
*(*float32)(unsafe.Pointer(uintptr(buf) + 4*i)) = buf32[i] *(*float32)(unsafe.Pointer(uintptr(buf) + 4*i)) = buf32[i]
} }
@ -285,6 +297,7 @@ type player struct {
type playerImpl struct { type playerImpl struct {
context *context context *context
players *players
src io.Reader src io.Reader
volume float64 volume float64
err error err error
@ -295,9 +308,14 @@ type playerImpl struct {
} }
func (c *context) NewPlayer(src io.Reader) Player { func (c *context) NewPlayer(src io.Reader) Player {
return newPlayer(c, c.players, src)
}
func newPlayer(context *context, players *players, src io.Reader) *player {
p := &player{ p := &player{
p: &playerImpl{ p: &playerImpl{
context: c, context: context,
players: players,
src: src, src: src,
volume: 1, volume: 1,
}, },
@ -351,7 +369,7 @@ func (p *playerImpl) playImpl() {
p.state = playerPlay p.state = playerPlay
p.m.Unlock() p.m.Unlock()
p.context.addPlayer(p) p.players.addPlayer(p)
p.m.Lock() p.m.Lock()
} }
@ -443,7 +461,7 @@ func (p *playerImpl) Close() error {
func (p *playerImpl) closeImpl() error { func (p *playerImpl) closeImpl() error {
p.m.Unlock() p.m.Unlock()
p.context.removePlayer(p) p.players.removePlayer(p)
p.m.Lock() p.m.Lock()
if p.state == playerClosed { if p.state == playerClosed {

View File

@ -322,11 +322,6 @@ func (p *playerImpl) playImpl() {
// Switching goroutines is very inefficient on Windows. Avoid a dedicated goroutine for a player. // Switching goroutines is very inefficient on Windows. Avoid a dedicated goroutine for a player.
} }
func volumeForWinAPI(v float64) uint32 {
u32 := uint32(0xffff * v)
return (u32 << 16) | u32
}
func (p *playerImpl) queuedHeadersNum() int { func (p *playerImpl) queuedHeadersNum() int {
var c int var c int
for _, h := range p.headers { for _, h := range p.headers {