examples/audio: Refactoring

This commit is contained in:
Hajime Hoshi 2018-01-22 02:15:04 +09:00
parent 3ee42c0c1e
commit 20e12a7ea9

View File

@ -47,6 +47,11 @@ var (
playerCurrentColor = color.RGBA{0xff, 0xff, 0xff, 0xff} playerCurrentColor = color.RGBA{0xff, 0xff, 0xff, 0xff}
) )
// Input manages the current input state to detect keys 'just pressed'.
//
// Note: 'just pressed' is a very common idiom.
// There is a plan to create a new package for input utility.
// See https://github.com/hajimehoshi/ebiten/issues/415.
type Input struct { type Input struct {
mouseButtonStates map[ebiten.MouseButton]int mouseButtonStates map[ebiten.MouseButton]int
keyStates map[ebiten.Key]int keyStates map[ebiten.Key]int
@ -67,7 +72,7 @@ func (i *Input) update() {
} }
} }
func (i *Input) isKeyTriggered(key ebiten.Key) bool { func (i *Input) isKeyJustPressed(key ebiten.Key) bool {
return i.keyStates[key] == 1 return i.keyStates[key] == 1
} }
@ -75,25 +80,22 @@ func (i *Input) isKeyPressed(key ebiten.Key) bool {
return i.keyStates[key] > 0 return i.keyStates[key] > 0
} }
func (i *Input) isMouseButtonTriggered(mouseButton ebiten.MouseButton) bool { func (i *Input) isMouseButtonJustPressed(mouseButton ebiten.MouseButton) bool {
return i.mouseButtonStates[mouseButton] == 1 return i.mouseButtonStates[mouseButton] == 1
} }
// Player represents the current audio state.
type Player struct { type Player struct {
input *Input input *Input
audioContext *audio.Context audioContext *audio.Context
audioPlayer *audio.Player audioPlayer *audio.Player
current time.Duration current time.Duration
total time.Duration total time.Duration
seBytes []uint8 seBytes []byte
seCh chan []uint8 seCh chan []byte
volume128 int volume128 int
} }
var (
musicPlayer *Player
)
func playerBarRect() (x, y, w, h int) { func playerBarRect() (x, y, w, h int) {
w, h = 300, 4 w, h = 300, 4
x = (screenWidth - w) / 2 x = (screenWidth - w) / 2
@ -128,7 +130,7 @@ func NewPlayer(audioContext *audio.Context) (*Player, error) {
audioPlayer: p, audioPlayer: p,
total: time.Second * time.Duration(s.Length()) / bytesPerSample / sampleRate, total: time.Second * time.Duration(s.Length()) / bytesPerSample / sampleRate,
volume128: 128, volume128: 128,
seCh: make(chan []uint8), seCh: make(chan []byte),
} }
if player.total == 0 { if player.total == 0 {
player.total = 1 player.total = 1
@ -158,32 +160,36 @@ func (p *Player) update() error {
p.seCh = nil p.seCh = nil
default: default:
} }
if p.audioPlayer.IsPlaying() { if p.audioPlayer.IsPlaying() {
p.current = p.audioPlayer.Current() p.current = p.audioPlayer.Current()
} }
p.updateBar() p.seekBarIfNeeded()
p.updatePlayPause() p.switchPlayStateIfNeeded()
p.updateSE() p.playSEIfNeeded()
p.updateVolume() p.updateVolumeIfNeeded()
if p.input.isKeyTriggered(ebiten.KeyB) {
if p.input.isKeyJustPressed(ebiten.KeyB) {
b := ebiten.IsRunnableInBackground() b := ebiten.IsRunnableInBackground()
ebiten.SetRunnableInBackground(!b) ebiten.SetRunnableInBackground(!b)
} }
return nil return nil
} }
func (p *Player) updateSE() { func (p *Player) playSEIfNeeded() {
if p.seBytes == nil { if p.seBytes == nil {
// Bytes for the SE is not loaded yet.
return return
} }
if !p.input.isKeyTriggered(ebiten.KeyP) {
if !p.input.isKeyJustPressed(ebiten.KeyP) {
return return
} }
sePlayer, _ := audio.NewPlayerFromBytes(p.audioContext, p.seBytes) sePlayer, _ := audio.NewPlayerFromBytes(p.audioContext, p.seBytes)
sePlayer.Play() sePlayer.Play()
} }
func (p *Player) updateVolume() { func (p *Player) updateVolumeIfNeeded() {
if p.input.isKeyPressed(ebiten.KeyZ) { if p.input.isKeyPressed(ebiten.KeyZ) {
p.volume128-- p.volume128--
} }
@ -199,8 +205,8 @@ func (p *Player) updateVolume() {
p.audioPlayer.SetVolume(float64(p.volume128) / 128) p.audioPlayer.SetVolume(float64(p.volume128) / 128)
} }
func (p *Player) updatePlayPause() { func (p *Player) switchPlayStateIfNeeded() {
if !p.input.isKeyTriggered(ebiten.KeyS) { if !p.input.isKeyJustPressed(ebiten.KeyS) {
return return
} }
if p.audioPlayer.IsPlaying() { if p.audioPlayer.IsPlaying() {
@ -210,11 +216,12 @@ func (p *Player) updatePlayPause() {
p.audioPlayer.Play() p.audioPlayer.Play()
} }
func (p *Player) updateBar() { func (p *Player) seekBarIfNeeded() {
if !p.input.isMouseButtonTriggered(ebiten.MouseButtonLeft) { if !p.input.isMouseButtonJustPressed(ebiten.MouseButtonLeft) {
return return
} }
// Start seeking.
// Calculate the next seeking position from the current cursor position.
x, y := ebiten.CursorPosition() x, y := ebiten.CursorPosition()
bx, by, bw, bh := playerBarRect() bx, by, bw, bh := playerBarRect()
const padding = 4 const padding = 4
@ -234,40 +241,45 @@ func (p *Player) close() error {
} }
func (p *Player) draw(screen *ebiten.Image) { func (p *Player) draw(screen *ebiten.Image) {
// Bar // Draw the bar.
x, y, w, h := playerBarRect() x, y, w, h := playerBarRect()
ebitenutil.DrawRect(screen, float64(x), float64(y), float64(w), float64(h), playerBarColor) ebitenutil.DrawRect(screen, float64(x), float64(y), float64(w), float64(h), playerBarColor)
currentTimeStr := "00:00" // Draw the cursor on the bar.
// Current Time
c := p.current c := p.current
m := (c / time.Minute) % 100
s := (c / time.Second) % 60
currentTimeStr = fmt.Sprintf("%02d:%02d", m, s)
// Cursor
cw, ch := 4, 10 cw, ch := 4, 10
cx := int(time.Duration(w)*c/p.total) + x - cw/2 cx := int(time.Duration(w)*c/p.total) + x - cw/2
cy := y - (ch-h)/2 cy := y - (ch-h)/2
ebitenutil.DrawRect(screen, float64(cx), float64(cy), float64(cw), float64(ch), playerCurrentColor) ebitenutil.DrawRect(screen, float64(cx), float64(cy), float64(cw), float64(ch), playerCurrentColor)
// Compose the curren time text.
m := (c / time.Minute) % 100
s := (c / time.Second) % 60
currentTimeStr := fmt.Sprintf("%02d:%02d", m, s)
// Draw the debug message.
msg := fmt.Sprintf(`FPS: %0.2f msg := fmt.Sprintf(`FPS: %0.2f
Press S to toggle Play/Pause Press S to toggle Play/Pause
Press P to play SE Press P to play SE
Press Z or X to change volume of the music Press Z or X to change volume of the music
Press B to switch the run-in-background state Press B to switch the run-in-background state
%s`, ebiten.CurrentFPS(), currentTimeStr) Current Time: %s`, ebiten.CurrentFPS(), currentTimeStr)
ebitenutil.DebugPrint(screen, msg) ebitenutil.DebugPrint(screen, msg)
} }
var (
musicPlayer *Player
)
func update(screen *ebiten.Image) error { func update(screen *ebiten.Image) error {
if err := musicPlayer.update(); err != nil { if err := musicPlayer.update(); err != nil {
return err return err
} }
if ebiten.IsRunningSlowly() { if ebiten.IsRunningSlowly() {
return nil return nil
} }
musicPlayer.draw(screen) musicPlayer.draw(screen)
return nil return nil
} }
@ -285,9 +297,7 @@ func main() {
if err := ebiten.Run(update, screenWidth, screenHeight, 2, "Audio (Ebiten Demo)"); err != nil { if err := ebiten.Run(update, screenWidth, screenHeight, 2, "Audio (Ebiten Demo)"); err != nil {
log.Fatal(err) log.Fatal(err)
} }
if musicPlayer != nil { if err := musicPlayer.close(); err != nil {
if err := musicPlayer.close(); err != nil { log.Fatal(err)
log.Fatal(err)
}
} }
} }