audio: Simplify player's Read/Seek

This commit is contained in:
Hajime Hoshi 2017-07-15 03:03:40 +09:00
parent 3cac19d1b8
commit 5fd8fe839b
4 changed files with 12 additions and 107 deletions

View File

@ -337,8 +337,6 @@ type Player struct {
players *players players *players
src ReadSeekCloser src ReadSeekCloser
sampleRate int sampleRate int
readingCh chan readingResult
seekCh chan int64
buf []uint8 buf []uint8
pos int64 pos int64
@ -366,7 +364,6 @@ func NewPlayer(context *Context, src ReadSeekCloser) (*Player, error) {
players: context.players, players: context.players,
src: src, src: src,
sampleRate: context.sampleRate, sampleRate: context.sampleRate,
seekCh: make(chan int64, 1),
buf: []uint8{}, buf: []uint8{},
volume: 1, volume: 1,
} }
@ -415,52 +412,21 @@ func (p *Player) Close() error {
} }
func (p *Player) readToBuffer(length int) (int, error) { func (p *Player) readToBuffer(length int) (int, error) {
if p.readingCh == nil { b := make([]uint8, length)
p.readingCh = make(chan readingResult) p.srcM.Lock()
go func() { n, err := p.src.Read(b)
b := make([]uint8, length) p.srcM.Unlock()
p.srcM.Lock() if err != nil {
n, err := p.src.Read(b) return 0, err
p.srcM.Unlock()
if err != nil {
p.readingCh <- readingResult{
err: err,
}
return
}
p.readingCh <- readingResult{
data: b[:n],
}
}()
}
select {
case pos := <-p.seekCh:
p.buf = []uint8{}
p.pos = pos
case r := <-p.readingCh:
close(p.readingCh)
p.readingCh = nil
if r.err != nil {
return 0, r.err
}
if len(r.data) > 0 {
p.buf = append(p.buf, r.data...)
}
case <-timeoutIfPossible(10 * time.Millisecond):
if l := length - len(p.buf); l > 0 {
empty := make([]uint8, l)
p.buf = append(p.buf, empty...)
}
} }
p.buf = append(p.buf, b[:n]...)
b = b[:n]
return len(p.buf), nil return len(p.buf), nil
} }
func (p *Player) bufferToInt16(lengthInBytes int) []int16 { func (p *Player) bufferToInt16(lengthInBytes int) []int16 {
r := make([]int16, lengthInBytes/2) r := make([]int16, lengthInBytes/2)
// This function must be called on the same goruotine of readToBuffer. // This function must be called on the same goruotine of readToBuffer.
if p.readingCh != nil {
return r
}
p.m.RLock() p.m.RLock()
for i := 0; i < lengthInBytes/2; i++ { for i := 0; i < lengthInBytes/2; i++ {
r[i] = int16(p.buf[2*i]) | (int16(p.buf[2*i+1]) << 8) r[i] = int16(p.buf[2*i]) | (int16(p.buf[2*i+1]) << 8)
@ -472,9 +438,6 @@ func (p *Player) bufferToInt16(lengthInBytes int) []int16 {
func (p *Player) proceed(length int) { func (p *Player) proceed(length int) {
// This function must be called on the same goruotine of readToBuffer. // This function must be called on the same goruotine of readToBuffer.
if p.readingCh != nil {
return
}
p.buf = p.buf[length:] p.buf = p.buf[length:]
p.pos += int64(length) p.pos += int64(length)
} }
@ -519,16 +482,8 @@ func (p *Player) Seek(offset time.Duration) error {
if err != nil { if err != nil {
return err return err
} }
// When the player p is not playing, as readToBuffer is never called, p.buf = []uint8{}
// seekCh will never solved. p.pos = pos
// Solve the current seeking here if necessary.
select {
case pos := <-p.seekCh:
p.buf = []uint8{}
p.pos = pos
default:
}
p.seekCh <- pos
return nil return nil
} }

View File

@ -21,6 +21,8 @@ import (
) )
func (c *Context) bufferSize() int { func (c *Context) bufferSize() int {
// TODO: examples/audio doesn't work well with 1/30[s],
// the other examples work though. Fix this.
n := 20 n := 20
if web.IsMobileBrowser() { if web.IsMobileBrowser() {
n = 10 n = 10

View File

@ -1,27 +0,0 @@
// Copyright 2017 The Ebiten Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// +build js
package audio
import (
"time"
)
func timeoutIfPossible(t time.Duration) <-chan time.Time {
// time.After uses setTimeout and causes performance problems.
// This function returns nil and blocks forever.
return nil
}

View File

@ -1,25 +0,0 @@
// Copyright 2017 The Ebiten Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// +build !js
package audio
import (
"time"
)
func timeoutIfPossible(t time.Duration) <-chan time.Time {
return time.After(t)
}