From 27911b815669a40fcc3d57c5a6e81796d05c118c Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 27 Mar 2016 19:10:16 +0900 Subject: [PATCH] Add audio/wav package --- examples/_resources/audio/jab.wav | Bin 0 -> 23708 bytes examples/_resources/audio/license.md | 13 +++- examples/audio/main.go | 89 +++++++++++++++++++++------ exp/audio/wav/decode.go | 86 ++++++++++++++++++++++++++ 4 files changed, 169 insertions(+), 19 deletions(-) create mode 100644 examples/_resources/audio/jab.wav create mode 100644 exp/audio/wav/decode.go diff --git a/examples/_resources/audio/jab.wav b/examples/_resources/audio/jab.wav new file mode 100644 index 0000000000000000000000000000000000000000..3f1206396f2a3ec3000b0f91ea06cf3f6998fc00 GIT binary patch literal 23708 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 literal 0 HcmV?d00001 diff --git a/examples/_resources/audio/license.md b/examples/_resources/audio/license.md index 41be7e44b..4a006f531 100644 --- a/examples/_resources/audio/license.md +++ b/examples/_resources/audio/license.md @@ -1,11 +1,22 @@ # License +## jab.wav + +``` +http://soundbible.com/995-Jab.html + +Recorded by Mike Koenig +Attribution 3.0: https://creativecommons.org/licenses/by/3.0/ +``` + ## ragtime.ogg ``` +https://soundcloud.com/jacaranda-trilhas-sonoras/james-scott-01-frog-legs-rag + Title: Frog Legs Rag (1906, piano roll) Artist: James Scott Album: Frog Legs: Ragtime Era Favorites Attribution-NonCommercial-ShareAlike: http://creativecommons.org/licenses/by-nc-sa/3.0/ -``` \ No newline at end of file +``` diff --git a/examples/audio/main.go b/examples/audio/main.go index ec05fb7aa..c8ae65959 100644 --- a/examples/audio/main.go +++ b/examples/audio/main.go @@ -15,8 +15,10 @@ package main import ( + "bytes" "fmt" "image/color" + "io" "log" "time" @@ -24,6 +26,7 @@ import ( "github.com/hajimehoshi/ebiten/ebitenutil" "github.com/hajimehoshi/ebiten/exp/audio" "github.com/hajimehoshi/ebiten/exp/audio/vorbis" + "github.com/hajimehoshi/ebiten/exp/audio/wav" ) const ( @@ -58,8 +61,10 @@ type Player struct { var ( audioContext *audio.Context - player *Player - playerCh = make(chan *Player) + musicPlayer *Player + seStream *wav.Stream + musicCh = make(chan *Player) + seCh = make(chan *wav.Stream) mouseButtonState = map[ebiten.MouseButton]int{} keyState = map[ebiten.Key]int{} ) @@ -71,6 +76,33 @@ func playerBarRect() (x, y, w, h int) { return } +func (p *Player) updateSE() error { + if seStream == nil { + return nil + } + if !ebiten.IsKeyPressed(ebiten.KeyP) { + keyState[ebiten.KeyP] = 0 + return nil + } + keyState[ebiten.KeyP]++ + if keyState[ebiten.KeyP] != 1 { + return nil + } + // Clone the stream + b := &bytes.Buffer{} + if _, err := seStream.Seek(0, 0); err != nil { + return err + } + if _, err := io.Copy(b, seStream); err != nil { + return err + } + sePlayer, err := audioContext.NewPlayer(bytes.NewReader(b.Bytes())) + if err != nil { + return err + } + return sePlayer.Play() +} + func (p *Player) updatePlayPause() error { if p.audioPlayer == nil { return nil @@ -115,17 +147,26 @@ func (p *Player) updateBar() error { func update(screen *ebiten.Image) error { audioContext.Update() - if player == nil { + if musicPlayer == nil { select { - case player = <-playerCh: + case musicPlayer = <-musicCh: default: } } - if player != nil { - if err := player.updateBar(); err != nil { + if seStream == nil { + select { + case seStream = <-seCh: + default: + } + } + if musicPlayer != nil { + if err := musicPlayer.updateBar(); err != nil { return err } - if err := player.updatePlayPause(); err != nil { + if err := musicPlayer.updatePlayPause(); err != nil { + return err + } + if err := musicPlayer.updateSE(); err != nil { return err } } @@ -135,8 +176,8 @@ func update(screen *ebiten.Image) error { op.GeoM.Translate(float64(x), float64(y)) screen.DrawImage(playerBarImage, op) currentTimeStr := "00:00" - if player != nil { - c := player.audioPlayer.Current() + if musicPlayer != nil { + c := musicPlayer.audioPlayer.Current() // Current Time m := (c / time.Minute) % 100 @@ -145,7 +186,7 @@ func update(screen *ebiten.Image) error { // Bar cw, ch := playerCurrentImage.Size() - cx := int(time.Duration(w)*c/player.total) + x - cw/2 + 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)) @@ -154,8 +195,9 @@ func update(screen *ebiten.Image) error { msg := fmt.Sprintf(`FPS: %0.2f Press S to toggle Play/Pause +Press P to play SE %s`, ebiten.CurrentFPS(), currentTimeStr) - if player == nil { + if musicPlayer == nil { msg += "\nNow Loading..." } ebitenutil.DebugPrint(screen, msg) @@ -163,16 +205,26 @@ Press S to toggle Play/Pause } func main() { - // Use a FLAC file so far: I couldn't find any good OGG/Vorbis decoder in pure Go. - f, err := ebitenutil.OpenFile("_resources/audio/ragtime.ogg") + 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) } - // TODO: sampleRate should be obtained from the ogg file. audioContext = audio.NewContext(22050) - // TODO: This doesn't work synchronously on browsers because of decoding. Fix this. go func() { - s, err := vorbis.Decode(audioContext, f) + s, err := wav.Decode(audioContext, wavF) + if err != nil { + log.Fatal(err) + return + } + seCh <- s + close(seCh) + }() + go func() { + s, err := vorbis.Decode(audioContext, oggF) if err != nil { log.Fatal(err) return @@ -182,11 +234,12 @@ func main() { log.Fatal(err) return } - playerCh <- &Player{ + musicCh <- &Player{ audioPlayer: p, total: s.Len(), } - close(playerCh) + close(musicCh) + // TODO: Is this goroutine-safe? p.Play() }() if err := ebiten.Run(update, screenWidth, screenHeight, 2, "Audio (Ebiten Demo)"); err != nil { diff --git a/exp/audio/wav/decode.go b/exp/audio/wav/decode.go new file mode 100644 index 000000000..5f4d229f0 --- /dev/null +++ b/exp/audio/wav/decode.go @@ -0,0 +1,86 @@ +// Copyright 2016 Hajime Hoshi +// +// 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. + +package wav + +import ( + "bytes" + "fmt" + "io" + "io/ioutil" + "time" + + "github.com/hajimehoshi/ebiten/exp/audio" +) + +const ( + headerSize = 44 + riffHeader = "RIFF" + waveHeader = "WAVE" +) + +type Stream struct { + buf *bytes.Reader + sampleRate int +} + +func (s *Stream) Read(p []byte) (int, error) { + return s.buf.Read(p) +} + +func (s *Stream) Seek(offset int64, whence int) (int64, error) { + return s.buf.Seek(offset, whence) +} + +func (s *Stream) Len() time.Duration { + const bytesPerSample = 4 + return time.Duration(s.buf.Len()/bytesPerSample) * time.Second / time.Duration(s.sampleRate) +} + +func Decode(context *audio.Context, src io.Reader) (*Stream, error) { + buf := make([]byte, headerSize) + n, err := io.ReadFull(src, buf) + if n != headerSize { + return nil, fmt.Errorf("wav: invalid header") + } + if err != nil { + return nil, err + } + if !bytes.Equal(buf[0:4], []byte(riffHeader)) { + return nil, fmt.Errorf("wav: invalid header: RIFF not found") + } + if !bytes.Equal(buf[8:12], []byte(waveHeader)) { + return nil, fmt.Errorf("wav: invalid header: WAVE not found") + } + channels, depth := buf[22], buf[34] + if channels != 2 { + return nil, fmt.Errorf("wav: invalid header: channel num must be 2") + } + if depth != 16 { + return nil, fmt.Errorf("wav: invalid header: depth must be 16") + } + sampleRate := int(buf[24]) | int(buf[25])<<8 | int(buf[26])<<16 | int(buf[27]<<24) + if context.SampleRate() != sampleRate { + return nil, fmt.Errorf("wav: sample rate must be %d but %d", context.SampleRate(), sampleRate) + } + b, err := ioutil.ReadAll(src) + if err != nil { + return nil, err + } + s := &Stream{ + buf: bytes.NewReader(b), + sampleRate: sampleRate, + } + return s, nil +}