mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-11-10 04:57:26 +01:00
audio/internal/readerplayer: Move the buffer for resuming to the driver side
Closes #1633
This commit is contained in:
parent
4e0e5c6bbc
commit
aa9f669ec3
@ -20,7 +20,6 @@ import (
|
||||
|
||||
type Context interface {
|
||||
NewPlayer(io.Reader) Player
|
||||
MaxBufferSize() int
|
||||
Suspend() error
|
||||
Resume() error
|
||||
io.Closer
|
||||
@ -59,7 +58,7 @@ func (c *context) oneBufferSize() int {
|
||||
|
||||
// maxBufferSize returns the maximum size of the buffer for the audio source.
|
||||
// This buffer is used when unreading on pausing the player.
|
||||
func (c *context) MaxBufferSize() int {
|
||||
func (c *context) maxBufferSize() int {
|
||||
// The number of underlying buffers should be 2.
|
||||
return c.oneBufferSize() * 2
|
||||
}
|
||||
|
@ -80,8 +80,22 @@ type player struct {
|
||||
}
|
||||
|
||||
func (p *player) Pause() {
|
||||
// TODO: Implement the 'true' pause after #1633 is fixed.
|
||||
p.Reset()
|
||||
p.cond.L.Lock()
|
||||
defer p.cond.L.Unlock()
|
||||
|
||||
if p.err != nil {
|
||||
return
|
||||
}
|
||||
if p.closed {
|
||||
return
|
||||
}
|
||||
if p.p == nil {
|
||||
return
|
||||
}
|
||||
if err := p.p.Pause(); err != nil {
|
||||
p.setErrorImpl(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (p *player) Play() {
|
||||
@ -224,10 +238,10 @@ func (p *player) shouldWait() bool {
|
||||
if p.p == nil {
|
||||
return false
|
||||
}
|
||||
if !p.p.IsPlaying() {
|
||||
return false
|
||||
if p.p.IsPlaying() {
|
||||
return p.p.UnplayedBufferSize() >= int64(p.context.maxBufferSize())
|
||||
}
|
||||
return p.p.UnplayedBufferSize() >= int64(p.context.MaxBufferSize())
|
||||
return true
|
||||
}
|
||||
|
||||
func (p *player) wait() bool {
|
||||
|
@ -137,11 +137,17 @@ func (p *player) Pause() {
|
||||
}
|
||||
|
||||
// Change the state first. appendBuffer is called as an 'ended' callback.
|
||||
var data [2][]float32
|
||||
for _, n := range p.bufferSourceNodes {
|
||||
for ch := 0; ch < 2; ch++ {
|
||||
t := n.Get("buffer").Call("getChannelData", ch)
|
||||
data[ch] = append(data[ch], float32ArrayToFloat32Slice(t)...)
|
||||
}
|
||||
n.Set("onended", nil)
|
||||
n.Call("stop")
|
||||
n.Call("disconnect")
|
||||
}
|
||||
p.buf = append(fromLR(data[0], data[1]), p.buf...)
|
||||
p.state = playerPaused
|
||||
p.bufferSourceNodes = p.bufferSourceNodes[:0]
|
||||
p.nextPos = 0
|
||||
@ -264,7 +270,7 @@ func (p *player) UnplayedBufferSize() int64 {
|
||||
for _, n := range p.bufferSourceNodes {
|
||||
sec += n.Get("buffer").Get("duration").Float()
|
||||
}
|
||||
return int64(sec * float64(p.context.sampleRate*p.context.channelNum*p.context.bitDepthInBytes))
|
||||
return int64(len(p.buf)) + int64(sec*float64(p.context.sampleRate*p.context.channelNum*p.context.bitDepthInBytes))
|
||||
}
|
||||
|
||||
func (p *player) Err() error {
|
||||
@ -287,10 +293,6 @@ func (w *go2cppDriverWrapper) NewPlayer(r io.Reader) Player {
|
||||
return w.c.NewPlayer(r)
|
||||
}
|
||||
|
||||
func (w *go2cppDriverWrapper) MaxBufferSize() int {
|
||||
return w.c.MaxBufferSize()
|
||||
}
|
||||
|
||||
func (w *go2cppDriverWrapper) Suspend() error {
|
||||
// Do nothing so far.
|
||||
return nil
|
||||
@ -317,6 +319,24 @@ func toLR(data []byte) ([]float32, []float32) {
|
||||
return l, r
|
||||
}
|
||||
|
||||
func fromLR(l, r []float32) []byte {
|
||||
const max = 1 << 15
|
||||
|
||||
if len(l) != len(r) {
|
||||
panic("readerdriver: len(l) must equal to len(r) at fromLR")
|
||||
}
|
||||
bs := make([]byte, len(l)*4)
|
||||
for i := range l {
|
||||
lv := int16(l[i] * max)
|
||||
bs[4*i] = byte(lv)
|
||||
bs[4*i+1] = byte(lv >> 8)
|
||||
rv := int16(r[i] * max)
|
||||
bs[4*i+2] = byte(rv)
|
||||
bs[4*i+3] = byte(rv >> 8)
|
||||
}
|
||||
return bs
|
||||
}
|
||||
|
||||
func float32SliceToTypedArray(s []float32) js.Value {
|
||||
h := (*reflect.SliceHeader)(unsafe.Pointer(&s))
|
||||
h.Len *= 4
|
||||
@ -329,3 +349,16 @@ func float32SliceToTypedArray(s []float32) js.Value {
|
||||
buf := a.Get("buffer")
|
||||
return js.Global().Get("Float32Array").New(buf, a.Get("byteOffset"), a.Get("byteLength").Int()/4)
|
||||
}
|
||||
|
||||
func float32ArrayToFloat32Slice(v js.Value) []float32 {
|
||||
bs := make([]byte, v.Get("byteLength").Int())
|
||||
js.CopyBytesToGo(bs, js.Global().Get("Uint8Array").New(v.Get("buffer"), v.Get("byteOffset"), v.Get("byteLength")))
|
||||
|
||||
h := (*reflect.SliceHeader)(unsafe.Pointer(&bs))
|
||||
h.Len /= 4
|
||||
h.Cap /= 4
|
||||
f32s := *(*[]float32)(unsafe.Pointer(h))
|
||||
runtime.KeepAlive(bs)
|
||||
|
||||
return f32s
|
||||
}
|
||||
|
@ -92,7 +92,7 @@ func (p *readerPlayer) ensurePlayer() error {
|
||||
p.factory.context = c
|
||||
}
|
||||
if p.stream == nil {
|
||||
s, err := newTimeStream(p.src, p.factory.sampleRate, p.factory.context.MaxBufferSize())
|
||||
s, err := newTimeStream(p.src, p.factory.sampleRate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -130,9 +130,7 @@ func (p *readerPlayer) Pause() {
|
||||
return
|
||||
}
|
||||
|
||||
n := p.player.UnplayedBufferSize()
|
||||
p.player.Pause()
|
||||
p.stream.Unread(int(n))
|
||||
p.context.removePlayer(p)
|
||||
}
|
||||
|
||||
@ -234,20 +232,16 @@ type timeStream struct {
|
||||
r io.Reader
|
||||
sampleRate int
|
||||
pos int64
|
||||
buf []byte
|
||||
unread int
|
||||
maxBufferSize int
|
||||
|
||||
// m is a mutex for this stream.
|
||||
// All the exported functions are protected by this mutex as Read can be read from a different goroutine than Seek.
|
||||
m sync.Mutex
|
||||
}
|
||||
|
||||
func newTimeStream(r io.Reader, sampleRate int, maxBufferSize int) (*timeStream, error) {
|
||||
func newTimeStream(r io.Reader, sampleRate int) (*timeStream, error) {
|
||||
s := &timeStream{
|
||||
r: r,
|
||||
sampleRate: sampleRate,
|
||||
maxBufferSize: maxBufferSize,
|
||||
}
|
||||
if seeker, ok := s.r.(io.Seeker); ok {
|
||||
// Get the current position of the source.
|
||||
@ -260,35 +254,12 @@ func newTimeStream(r io.Reader, sampleRate int, maxBufferSize int) (*timeStream,
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (s *timeStream) Unread(n int) {
|
||||
s.m.Lock()
|
||||
defer s.m.Unlock()
|
||||
|
||||
if s.unread+n > len(s.buf) {
|
||||
// This should not happen usually, but the player's UnplayedBufferSize can include some errors.
|
||||
n = len(s.buf) - s.unread
|
||||
}
|
||||
s.unread += n
|
||||
s.pos -= int64(n)
|
||||
}
|
||||
|
||||
func (s *timeStream) Read(buf []byte) (int, error) {
|
||||
s.m.Lock()
|
||||
defer s.m.Unlock()
|
||||
|
||||
if s.unread > 0 {
|
||||
n := copy(buf, s.buf[len(s.buf)-s.unread:])
|
||||
s.unread -= n
|
||||
s.pos += int64(n)
|
||||
return n, nil
|
||||
}
|
||||
|
||||
n, err := s.r.Read(buf)
|
||||
s.pos += int64(n)
|
||||
s.buf = append(s.buf, buf[:n]...)
|
||||
if m := s.maxBufferSize; len(s.buf) > m {
|
||||
s.buf = s.buf[len(s.buf)-m:]
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
@ -312,8 +283,6 @@ func (s *timeStream) Seek(offset time.Duration) error {
|
||||
}
|
||||
|
||||
s.pos = pos
|
||||
s.buf = s.buf[:0]
|
||||
s.unread = 0
|
||||
return nil
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user