mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-25 11:18:54 +01:00
audio/internal/go2cpp: Replace isWritable with unplayedBufferSize instead
This is the more accurate way not to overflow the underlying buffer.
This commit is contained in:
parent
6f185063d6
commit
1083233d5f
@ -21,6 +21,10 @@ import (
|
||||
"syscall/js"
|
||||
)
|
||||
|
||||
const (
|
||||
readChunkSize = 4096
|
||||
)
|
||||
|
||||
type Context struct {
|
||||
v js.Value
|
||||
sampleRate int
|
||||
@ -96,6 +100,16 @@ func (p *Player) Pause() {
|
||||
p.cond.Signal()
|
||||
}
|
||||
|
||||
func (p *Player) oneBufferSize() int {
|
||||
// TODO: This must be audio.oneBufferSize(p.context.sampleRate). Avoid the duplication.
|
||||
return p.context.sampleRate * p.context.channelNum * p.context.bitDepthInBytes / 4
|
||||
}
|
||||
|
||||
func (p *Player) maxBufferSize() int {
|
||||
// TODO: This must be audio.maxBufferSize(p.context.sampleRate). Avoid the duplication.
|
||||
return p.oneBufferSize() * 2
|
||||
}
|
||||
|
||||
func (p *Player) Play() {
|
||||
p.cond.L.Lock()
|
||||
defer p.cond.L.Unlock()
|
||||
@ -116,7 +130,11 @@ func (p *Player) Play() {
|
||||
// Prepare the first data as soon as possible, or the audio can get stuck.
|
||||
// TODO: Get the appropriate buffer size from the C++ side.
|
||||
if p.buf == nil {
|
||||
p.buf = make([]byte, p.context.sampleRate*p.context.channelNum*p.context.bitDepthInBytes/4)
|
||||
n := p.oneBufferSize()
|
||||
if max := p.maxBufferSize() - int(p.UnplayedBufferSize()); n > max {
|
||||
n = max
|
||||
}
|
||||
p.buf = make([]byte, n)
|
||||
}
|
||||
n, err := p.src.Read(p.buf)
|
||||
if err != nil && err != io.EOF {
|
||||
@ -176,6 +194,9 @@ func (p *Player) SetVolume(volume float64) {
|
||||
}
|
||||
|
||||
func (p *Player) UnplayedBufferSize() int64 {
|
||||
if !p.v.Truthy() {
|
||||
return 0
|
||||
}
|
||||
return int64(p.v.Get("unplayedBufferSize").Int())
|
||||
}
|
||||
|
||||
@ -219,11 +240,24 @@ func (p *Player) setError(err error) {
|
||||
p.cond.Signal()
|
||||
}
|
||||
|
||||
func (p *Player) shouldWait() bool {
|
||||
if !p.v.Truthy() {
|
||||
return false
|
||||
}
|
||||
switch p.state {
|
||||
case playerStatePaused:
|
||||
return true
|
||||
case playerStatePlaying:
|
||||
return p.v.Get("unplayedBufferSize").Int() >= p.maxBufferSize()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *Player) waitUntilUnpaused() bool {
|
||||
p.cond.L.Lock()
|
||||
defer p.cond.L.Unlock()
|
||||
|
||||
for p.v.Truthy() && (p.state == playerStatePaused || (p.state == playerStatePlaying && !p.v.Call("isWritable").Bool())) {
|
||||
for p.shouldWait() {
|
||||
p.cond.Wait()
|
||||
}
|
||||
return p.v.Truthy() && p.state == playerStatePlaying
|
||||
@ -248,23 +282,25 @@ func (p *Player) writeImpl(dst js.Value, src []byte) {
|
||||
}
|
||||
|
||||
func (p *Player) loop() {
|
||||
const size = 4096
|
||||
|
||||
buf := make([]byte, size)
|
||||
dst := js.Global().Get("Uint8Array").New(size)
|
||||
buf := make([]byte, readChunkSize)
|
||||
dst := js.Global().Get("Uint8Array").New(readChunkSize)
|
||||
|
||||
for {
|
||||
if !p.waitUntilUnpaused() {
|
||||
return
|
||||
}
|
||||
|
||||
n, err := p.src.Read(buf)
|
||||
n := readChunkSize
|
||||
if max := p.maxBufferSize() - int(p.UnplayedBufferSize()); n > max {
|
||||
n = max
|
||||
}
|
||||
n2, err := p.src.Read(buf[:n])
|
||||
if err != nil && err != io.EOF {
|
||||
p.setError(err)
|
||||
return
|
||||
}
|
||||
if n > 0 {
|
||||
p.write(dst, buf[:n])
|
||||
p.write(dst, buf[:n2])
|
||||
}
|
||||
|
||||
if err == io.EOF {
|
||||
|
@ -32,11 +32,8 @@ func oneBufferSize(sampleRate int) int {
|
||||
// maxBufferSize returns the maximum size of the buffer for the audio source.
|
||||
// This buffer is used when unreading on pausing the player.
|
||||
func maxBufferSize(sampleRate int) int {
|
||||
// Actually *2 should be enough in most cases,
|
||||
// but in some implementation (e.g, go2cpp), a player might have more UnplayedBufferSize values.
|
||||
// As a safe margin, use *4 value.
|
||||
// TODO: Ensure the maximum value of UnplayedBufferSize on all the platforms.
|
||||
return oneBufferSize(sampleRate) * 4
|
||||
// The number of underlying buffers should be 2.
|
||||
return oneBufferSize(sampleRate) * 2
|
||||
}
|
||||
|
||||
// readerDriver represents a driver using io.ReadClosers.
|
||||
|
Loading…
Reference in New Issue
Block a user