audio/internal/go2cpp: Prepare the first buffer data to make the audio smoother

This commit is contained in:
Hajime Hoshi 2021-01-16 03:28:47 +09:00
parent c742ae60bd
commit ed7d7e8976
3 changed files with 37 additions and 8 deletions

View File

@ -44,8 +44,9 @@ import (
) )
const ( const (
channelNum = 2 channelNum = 2
bytesPerSample = 2 * channelNum bitDepthInBytes = 2
bytesPerSample = bitDepthInBytes * channelNum
) )
type newPlayerImpler interface { type newPlayerImpler interface {

View File

@ -22,13 +22,19 @@ import (
) )
type Context struct { type Context struct {
v js.Value v js.Value
sampleRate int
channelNum int
bitDepthInBytes int
} }
func NewContext(sampleRate int) *Context { func NewContext(sampleRate int, channelNum, bitDepthInBytes int) *Context {
v := js.Global().Get("go2cpp").Call("createAudio", sampleRate, 2, 2) v := js.Global().Get("go2cpp").Call("createAudio", sampleRate, channelNum, bitDepthInBytes)
return &Context{ return &Context{
v: v, v: v,
sampleRate: sampleRate,
channelNum: channelNum,
bitDepthInBytes: bitDepthInBytes,
} }
} }
@ -96,13 +102,32 @@ func (p *Player) Play() {
if p.state == playerStateClosed { if p.state == playerStateClosed {
return return
} }
var runloop bool
if !p.v.Truthy() { if !p.v.Truthy() {
p.v = p.context.v.Call("createPlayer", p.onWritten) p.v = p.context.v.Call("createPlayer", p.onWritten)
p.v.Set("volume", p.volume) p.v.Set("volume", p.volume)
go p.loop() runloop = true
} }
p.v.Call("play") p.v.Call("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.
buf := make([]byte, p.context.sampleRate*p.context.channelNum*p.context.bitDepthInBytes/4)
n, err := p.src.Read(buf)
if err != nil && err != io.EOF {
p.setError(err)
return
}
if n > 0 {
dst := js.Global().Get("Uint8Array").New(n)
p.writeImpl(dst, buf[:n])
}
if runloop {
go p.loop()
}
p.state = playerStatePlaying p.state = playerStatePlaying
p.cond.Signal() p.cond.Signal()
} }
@ -198,7 +223,10 @@ func (p *Player) waitUntilUnpaused() bool {
func (p *Player) write(dst js.Value, src []byte) { func (p *Player) write(dst js.Value, src []byte) {
p.cond.L.Lock() p.cond.L.Lock()
defer p.cond.L.Unlock() defer p.cond.L.Unlock()
p.writeImpl(dst, src)
}
func (p *Player) writeImpl(dst js.Value, src []byte) {
if p.state == playerStateClosed { if p.state == playerStateClosed {
return return
} }

View File

@ -26,7 +26,7 @@ func isReaderContextAvailable() bool {
} }
func newReaderDriverImpl(sampleRate int) readerDriver { func newReaderDriverImpl(sampleRate int) readerDriver {
return &go2cppDriverWrapper{go2cpp.NewContext(sampleRate)} return &go2cppDriverWrapper{go2cpp.NewContext(sampleRate, channelNum, bitDepthInBytes)}
} }
type go2cppDriverWrapper struct { type go2cppDriverWrapper struct {