From ed7d7e89767f7176f62a221eb7e4fd9854939354 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sat, 16 Jan 2021 03:28:47 +0900 Subject: [PATCH] audio/internal/go2cpp: Prepare the first buffer data to make the audio smoother --- audio/audio.go | 5 ++-- audio/internal/go2cpp/player_js.go | 38 ++++++++++++++++++++++++++---- audio/readerplayer_js.go | 2 +- 3 files changed, 37 insertions(+), 8 deletions(-) diff --git a/audio/audio.go b/audio/audio.go index 54e8f5fc3..164691aaf 100644 --- a/audio/audio.go +++ b/audio/audio.go @@ -44,8 +44,9 @@ import ( ) const ( - channelNum = 2 - bytesPerSample = 2 * channelNum + channelNum = 2 + bitDepthInBytes = 2 + bytesPerSample = bitDepthInBytes * channelNum ) type newPlayerImpler interface { diff --git a/audio/internal/go2cpp/player_js.go b/audio/internal/go2cpp/player_js.go index 2bcbe29e3..ad29ca004 100644 --- a/audio/internal/go2cpp/player_js.go +++ b/audio/internal/go2cpp/player_js.go @@ -22,13 +22,19 @@ import ( ) type Context struct { - v js.Value + v js.Value + sampleRate int + channelNum int + bitDepthInBytes int } -func NewContext(sampleRate int) *Context { - v := js.Global().Get("go2cpp").Call("createAudio", sampleRate, 2, 2) +func NewContext(sampleRate int, channelNum, bitDepthInBytes int) *Context { + v := js.Global().Get("go2cpp").Call("createAudio", sampleRate, channelNum, bitDepthInBytes) return &Context{ - v: v, + v: v, + sampleRate: sampleRate, + channelNum: channelNum, + bitDepthInBytes: bitDepthInBytes, } } @@ -96,13 +102,32 @@ func (p *Player) Play() { if p.state == playerStateClosed { return } + + var runloop bool if !p.v.Truthy() { p.v = p.context.v.Call("createPlayer", p.onWritten) p.v.Set("volume", p.volume) - go p.loop() + runloop = true } 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.cond.Signal() } @@ -198,7 +223,10 @@ func (p *Player) waitUntilUnpaused() bool { func (p *Player) write(dst js.Value, src []byte) { p.cond.L.Lock() defer p.cond.L.Unlock() + p.writeImpl(dst, src) +} +func (p *Player) writeImpl(dst js.Value, src []byte) { if p.state == playerStateClosed { return } diff --git a/audio/readerplayer_js.go b/audio/readerplayer_js.go index 8a00d77d1..bc7429892 100644 --- a/audio/readerplayer_js.go +++ b/audio/readerplayer_js.go @@ -26,7 +26,7 @@ func isReaderContextAvailable() bool { } func newReaderDriverImpl(sampleRate int) readerDriver { - return &go2cppDriverWrapper{go2cpp.NewContext(sampleRate)} + return &go2cppDriverWrapper{go2cpp.NewContext(sampleRate, channelNum, bitDepthInBytes)} } type go2cppDriverWrapper struct {