mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-26 11:48:55 +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"
|
"syscall/js"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
readChunkSize = 4096
|
||||||
|
)
|
||||||
|
|
||||||
type Context struct {
|
type Context struct {
|
||||||
v js.Value
|
v js.Value
|
||||||
sampleRate int
|
sampleRate int
|
||||||
@ -96,6 +100,16 @@ func (p *Player) Pause() {
|
|||||||
p.cond.Signal()
|
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() {
|
func (p *Player) Play() {
|
||||||
p.cond.L.Lock()
|
p.cond.L.Lock()
|
||||||
defer p.cond.L.Unlock()
|
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.
|
// Prepare the first data as soon as possible, or the audio can get stuck.
|
||||||
// TODO: Get the appropriate buffer size from the C++ side.
|
// TODO: Get the appropriate buffer size from the C++ side.
|
||||||
if p.buf == nil {
|
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)
|
n, err := p.src.Read(p.buf)
|
||||||
if err != nil && err != io.EOF {
|
if err != nil && err != io.EOF {
|
||||||
@ -176,6 +194,9 @@ func (p *Player) SetVolume(volume float64) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *Player) UnplayedBufferSize() int64 {
|
func (p *Player) UnplayedBufferSize() int64 {
|
||||||
|
if !p.v.Truthy() {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
return int64(p.v.Get("unplayedBufferSize").Int())
|
return int64(p.v.Get("unplayedBufferSize").Int())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -219,11 +240,24 @@ func (p *Player) setError(err error) {
|
|||||||
p.cond.Signal()
|
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 {
|
func (p *Player) waitUntilUnpaused() bool {
|
||||||
p.cond.L.Lock()
|
p.cond.L.Lock()
|
||||||
defer p.cond.L.Unlock()
|
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()
|
p.cond.Wait()
|
||||||
}
|
}
|
||||||
return p.v.Truthy() && p.state == playerStatePlaying
|
return p.v.Truthy() && p.state == playerStatePlaying
|
||||||
@ -248,23 +282,25 @@ func (p *Player) writeImpl(dst js.Value, src []byte) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *Player) loop() {
|
func (p *Player) loop() {
|
||||||
const size = 4096
|
buf := make([]byte, readChunkSize)
|
||||||
|
dst := js.Global().Get("Uint8Array").New(readChunkSize)
|
||||||
buf := make([]byte, size)
|
|
||||||
dst := js.Global().Get("Uint8Array").New(size)
|
|
||||||
|
|
||||||
for {
|
for {
|
||||||
if !p.waitUntilUnpaused() {
|
if !p.waitUntilUnpaused() {
|
||||||
return
|
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 {
|
if err != nil && err != io.EOF {
|
||||||
p.setError(err)
|
p.setError(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
p.write(dst, buf[:n])
|
p.write(dst, buf[:n2])
|
||||||
}
|
}
|
||||||
|
|
||||||
if err == io.EOF {
|
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.
|
// maxBufferSize returns the maximum size of the buffer for the audio source.
|
||||||
// This buffer is used when unreading on pausing the player.
|
// This buffer is used when unreading on pausing the player.
|
||||||
func maxBufferSize(sampleRate int) int {
|
func maxBufferSize(sampleRate int) int {
|
||||||
// Actually *2 should be enough in most cases,
|
// The number of underlying buffers should be 2.
|
||||||
// but in some implementation (e.g, go2cpp), a player might have more UnplayedBufferSize values.
|
return oneBufferSize(sampleRate) * 2
|
||||||
// As a safe margin, use *4 value.
|
|
||||||
// TODO: Ensure the maximum value of UnplayedBufferSize on all the platforms.
|
|
||||||
return oneBufferSize(sampleRate) * 4
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// readerDriver represents a driver using io.ReadClosers.
|
// readerDriver represents a driver using io.ReadClosers.
|
||||||
|
Loading…
Reference in New Issue
Block a user