examples/audio: Implement seeking

This commit is contained in:
Hajime Hoshi 2016-03-20 01:40:10 +09:00
parent 0fb5dffbf5
commit 4a4621a126
2 changed files with 46 additions and 11 deletions

View File

@ -57,8 +57,40 @@ var (
audioLoaded bool audioLoaded bool
audioPlayer *audio.Player audioPlayer *audio.Player
total time.Duration total time.Duration
mouseButtonState = map[ebiten.MouseButton]int{}
) )
func playerBarRect() (x, y, w, h int) {
w, h = playerBarImage.Size()
x = (screenWidth - w) / 2
y = screenHeight - h - 16
return
}
func updatePlayerBar() error {
if !audioLoaded {
return nil
}
if !ebiten.IsMouseButtonPressed(ebiten.MouseButtonLeft) {
mouseButtonState[ebiten.MouseButtonLeft] = 0
return nil
}
mouseButtonState[ebiten.MouseButtonLeft]++
if mouseButtonState[ebiten.MouseButtonLeft] != 1 {
return nil
}
x, y := ebiten.CursorPosition()
bx, by, bw, bh := playerBarRect()
if y < by || by+bh <= y {
return nil
}
if x < bx || bx+bw <= x {
return nil
}
p := time.Duration(x-bx) * total / time.Duration(bw)
return audioPlayer.Seek(p)
}
func update(screen *ebiten.Image) error { func update(screen *ebiten.Image) error {
audioContext.Update() audioContext.Update()
if !audioLoaded { if !audioLoaded {
@ -69,11 +101,13 @@ func update(screen *ebiten.Image) error {
} }
} }
if err := updatePlayerBar(); err != nil {
return err
}
op := &ebiten.DrawImageOptions{} op := &ebiten.DrawImageOptions{}
w, h := playerBarImage.Size() x, y, w, h := playerBarRect()
x := float64(screenWidth-w) / 2 op.GeoM.Translate(float64(x), float64(y))
y := float64(screenHeight - h - 16)
op.GeoM.Translate(x, y)
screen.DrawImage(playerBarImage, op) screen.DrawImage(playerBarImage, op)
currentTimeStr := "" currentTimeStr := ""
if audioLoaded && audioPlayer.IsPlaying() { if audioLoaded && audioPlayer.IsPlaying() {
@ -85,12 +119,11 @@ func update(screen *ebiten.Image) error {
currentTimeStr = fmt.Sprintf("%02d:%02d", m, s) currentTimeStr = fmt.Sprintf("%02d:%02d", m, s)
// Bar // Bar
w, _ := playerBarImage.Size()
cw, ch := playerCurrentImage.Size() cw, ch := playerCurrentImage.Size()
cx := float64(w)*float64(c)/float64(total) + x - float64(cw)/2 cx := int(time.Duration(w)*c/total) + x - cw/2
cy := y - float64(ch-h)/2 cy := y - (ch-h)/2
op := &ebiten.DrawImageOptions{} op := &ebiten.DrawImageOptions{}
op.GeoM.Translate(cx, cy) op.GeoM.Translate(float64(cx), float64(cy))
screen.DrawImage(playerCurrentImage, op) screen.DrawImage(playerCurrentImage, op)
} }

View File

@ -39,6 +39,9 @@ const (
channelNum = 2 channelNum = 2
bytesPerSample = 2 bytesPerSample = 2
bitsPerSample = bytesPerSample * 8 bitsPerSample = bytesPerSample * 8
// TODO: This assumes that channelNum is a power of 2.
mask = ^(channelNum*bytesPerSample - 1)
) )
func (s *mixedPlayersStream) Read(b []byte) (int, error) { func (s *mixedPlayersStream) Read(b []byte) (int, error) {
@ -51,8 +54,6 @@ func (s *mixedPlayersStream) Read(b []byte) (int, error) {
return 0, nil return 0, nil
} }
// TODO: This assumes that channelNum is a power of 2.
const mask = ^(channelNum*bytesPerSample - 1)
if len(s.context.players) == 0 { if len(s.context.players) == 0 {
l := min(len(b), x-s.writtenBytes) l := min(len(b), x-s.writtenBytes)
l &= mask l &= mask
@ -222,7 +223,8 @@ func (p *Player) Rewind() error {
func (p *Player) Seek(offset time.Duration) error { func (p *Player) Seek(offset time.Duration) error {
p.buf = []byte{} p.buf = []byte{}
o := int64(offset) * int64(p.context.sampleRate) / int64(time.Second) o := int64(offset) * bytesPerSample * channelNum * int64(p.context.sampleRate) / int64(time.Second)
o &= mask
pos, err := p.src.Seek(o, 0) pos, err := p.src.Seek(o, 0)
if err != nil { if err != nil {
return err return err