<!DOCTYPE html> <script src="../scripts/force-https.js"></script> <link rel="shortcut icon" href="../favicon.png" type="image/png" > <link rel="icon" href="../favicon.png" type="image/png" > <title>Ebiten example - audio</title> <link rel="stylesheet" href="../stylesheets/bootstrap.min.css"> <link rel="stylesheet" href="../stylesheets/highlight-github.css"> <link rel="stylesheet" href="../stylesheets/ebiten.css"> <header class="navbar"><div class="container"> <div class="navbar-header"> <a class="navbar-brand" href="..">Ebiten</a> </div> <nav class="collapse navbar-collapse"> <ul class="nav navbar-nav navbar-right"> <li><a href="https://github.com/hajimehoshi/ebiten">GitHub</a></li> <li><a href="https://godoc.org/github.com/hajimehoshi/ebiten">GoDoc</a></li> <li><a href="https://github.com/hajimehoshi/ebiten/wiki">Wiki</a> </ul> </nav> </header> <main><div class="container"> <h2>Ebiten example - audio</h2> <iframe src="audio.content.html" width="640" height="480"></iframe> <pre><code class="language-go">// +build example package main import ( "fmt" "image/color" "io/ioutil" "log" "time" "github.com/hajimehoshi/ebiten" "github.com/hajimehoshi/ebiten/audio" "github.com/hajimehoshi/ebiten/audio/vorbis" "github.com/hajimehoshi/ebiten/audio/wav" "github.com/hajimehoshi/ebiten/ebitenutil" ) const ( screenWidth = 320 screenHeight = 240 ) var ( playerBarImage *ebiten.Image playerCurrentImage *ebiten.Image ) func init() { var err error playerBarImage, err = ebiten.NewImage(300, 4, ebiten.FilterNearest) if err != nil { log.Fatal(err) } if err := playerBarImage.Fill(&color.RGBA{0x80, 0x80, 0x80, 0xff}); err != nil { log.Fatal(err) } playerCurrentImage, err = ebiten.NewImage(4, 10, ebiten.FilterNearest) if err != nil { log.Fatal(err) } if err := playerCurrentImage.Fill(&color.RGBA{0xff, 0xff, 0xff, 0xff}); err != nil { log.Fatal(err) } } type Player struct { audioPlayer *audio.Player total time.Duration } var ( audioContext *audio.Context musicPlayer *Player seBytes []byte musicCh = make(chan *Player) seCh = make(chan []byte) mouseButtonState = map[ebiten.MouseButton]int{} keyState = map[ebiten.Key]int{} volume128 = 128 ) func playerBarRect() (x, y, w, h int) { w, h = playerBarImage.Size() x = (screenWidth - w) / 2 y = screenHeight - h - 16 return } func (p *Player) updateSE() error { if seBytes == nil { return nil } if !ebiten.IsKeyPressed(ebiten.KeyP) { keyState[ebiten.KeyP] = 0 return nil } keyState[ebiten.KeyP]++ if keyState[ebiten.KeyP] != 1 { return nil } sePlayer, err := audio.NewPlayerFromBytes(audioContext, seBytes) if err != nil { return err } return sePlayer.Play() } func (p *Player) updateVolume() error { if p.audioPlayer == nil { return nil } if ebiten.IsKeyPressed(ebiten.KeyZ) { volume128-- } if ebiten.IsKeyPressed(ebiten.KeyX) { volume128++ } if volume128 < 0 { volume128 = 0 } if 128 < volume128 { volume128 = 128 } p.audioPlayer.SetVolume(float64(volume128) / 128) return nil } func (p *Player) updatePlayPause() error { if p.audioPlayer == nil { return nil } if !ebiten.IsKeyPressed(ebiten.KeyS) { keyState[ebiten.KeyS] = 0 return nil } keyState[ebiten.KeyS]++ if keyState[ebiten.KeyS] != 1 { return nil } if p.audioPlayer.IsPlaying() { return p.audioPlayer.Pause() } return p.audioPlayer.Play() } func (p *Player) updateBar() error { if p.audioPlayer == nil { 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() const padding = 4 if y < by-padding || by+bh+padding <= y { return nil } if x < bx || bx+bw <= x { return nil } pos := time.Duration(x-bx) * p.total / time.Duration(bw) return p.audioPlayer.Seek(pos) } func (p *Player) close() error { return p.audioPlayer.Close() } func update(screen *ebiten.Image) error { if musicPlayer == nil { select { case musicPlayer = <-musicCh: default: } } if seBytes == nil { select { case seBytes = <-seCh: default: } } if musicPlayer != nil { if err := musicPlayer.updateBar(); err != nil { return err } if err := musicPlayer.updatePlayPause(); err != nil { return err } if err := musicPlayer.updateSE(); err != nil { return err } if err := musicPlayer.updateVolume(); err != nil { return err } } op := &ebiten.DrawImageOptions{} x, y, w, h := playerBarRect() op.GeoM.Translate(float64(x), float64(y)) if err := screen.DrawImage(playerBarImage, op); err != nil { return err } currentTimeStr := "00:00" if musicPlayer != nil { c := musicPlayer.audioPlayer.Current() // Current Time m := (c / time.Minute) % 100 s := (c / time.Second) % 60 currentTimeStr = fmt.Sprintf("%02d:%02d", m, s) // Bar cw, ch := playerCurrentImage.Size() cx := int(time.Duration(w)*c/musicPlayer.total) + x - cw/2 cy := y - (ch-h)/2 op := &ebiten.DrawImageOptions{} op.GeoM.Translate(float64(cx), float64(cy)) if err := screen.DrawImage(playerCurrentImage, op); err != nil { return err } } msg := fmt.Sprintf(`FPS: %0.2f Press S to toggle Play/Pause Press P to play SE Press Z or X to change volume of the music %s`, ebiten.CurrentFPS(), currentTimeStr) if musicPlayer == nil { msg += "\nNow Loading..." } if err := ebitenutil.DebugPrint(screen, msg); err != nil { return err } if err := audioContext.Update(); err != nil { return err } return nil } func main() { wavF, err := ebitenutil.OpenFile("_resources/audio/jab.wav") if err != nil { log.Fatal(err) } oggF, err := ebitenutil.OpenFile("_resources/audio/ragtime.ogg") if err != nil { log.Fatal(err) } const sampleRate = 22050 const bytesPerSample = 4 // TODO: This should be defined in audio package audioContext, err = audio.NewContext(sampleRate) if err != nil { log.Fatal(err) } go func() { s, err := wav.Decode(audioContext, wavF) if err != nil { log.Fatal(err) return } b, err := ioutil.ReadAll(s) if err != nil { log.Fatal(err) return } seCh <- b close(seCh) }() go func() { s, err := vorbis.Decode(audioContext, oggF) if err != nil { log.Fatal(err) return } p, err := audio.NewPlayer(audioContext, s) if err != nil { log.Fatal(err) return } musicCh <- &Player{ audioPlayer: p, total: time.Second * time.Duration(s.Size()) / bytesPerSample / sampleRate, } close(musicCh) // TODO: Is this goroutine-safe? if err := p.Play(); err != nil { log.Fatal(err) return } }() if err := ebiten.Run(update, screenWidth, screenHeight, 2, "Audio (Ebiten Demo)"); err != nil { log.Fatal(err) } if musicPlayer != nil { if err := musicPlayer.close(); err != nil { log.Fatal(err) } } } </code></pre> </div></main> <footer><div class="container"> <p>© 2013 Hajime Hoshi</p> <p>Code is licensed under <a href="https://github.com/hajimehoshi/ebiten/blob/master/LICENSE">Apache License 2.0</a>.</p> <p>The content of this page is licensed under <a href="https://creativecommons.org/licenses/by/4.0/">the Creative Commons Attribution 4.0 License</a>.</p> </div></footer> <script src="../scripts/highlight.pack.js"></script> <script>hljs.initHighlightingOnLoad();</script> <script> (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) })(window,document,'script','https://www.google-analytics.com/analytics.js','ga'); ga('create', 'UA-83252440-1', 'auto'); ga('send', 'pageview'); </script>