From de59c932190c84c22461ba552f9d31f245d331cb Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Mon, 8 Jun 2015 00:04:21 +0900 Subject: [PATCH] audio: Change API to accept raw PCM data --- example/audio/main.go | 22 +++++++++++++++++++--- example/piano/main.go | 36 +++++++++++++++++++++++++----------- exp/audio/audio.go | 30 ++++++++++++++++++++++++++++-- 3 files changed, 72 insertions(+), 16 deletions(-) diff --git a/example/audio/main.go b/example/audio/main.go index b3a82fcee..523b361fc 100644 --- a/example/audio/main.go +++ b/example/audio/main.go @@ -15,12 +15,15 @@ package main import ( + "bytes" + "encoding/binary" "fmt" + "log" + "math" + "github.com/hajimehoshi/ebiten" "github.com/hajimehoshi/ebiten/ebitenutil" "github.com/hajimehoshi/ebiten/exp/audio" - "log" - "math" ) const ( @@ -69,6 +72,19 @@ func square(out []int16, volume float64, freq float64, sequence float64) { } } +func toBytes(l, r []int16) []byte { + if len(l) != len(r) { + panic("len(l) must equal to len(r)") + } + b := &bytes.Buffer{} + for i := 0; i < len(l); i++ { + if err := binary.Write(b, binary.LittleEndian, []int16{l[i], r[i]}); err != nil { + panic(err) + } + } + return b.Bytes() +} + func addNote() { size := audio.SampleRate() / 60 notes := []float64{freqC, freqD, freqE, freqF, freqG, freqA * 2, freqB * 2} @@ -97,7 +113,7 @@ func addNote() { vol := 1.0 / 16.0 square(l, vol, freq, 0.25) square(r, vol, freq, 0.25) - audio.Play(0, l, r) + audio.Play(0, toBytes(l, r)) } func update(screen *ebiten.Image) error { diff --git a/example/piano/main.go b/example/piano/main.go index bcb34879c..5c3a8a8dc 100644 --- a/example/piano/main.go +++ b/example/piano/main.go @@ -15,14 +15,17 @@ package main import ( + "bytes" + "encoding/binary" "fmt" + "image/color" + "log" + "math" + "github.com/hajimehoshi/ebiten" "github.com/hajimehoshi/ebiten/ebitenutil" "github.com/hajimehoshi/ebiten/example/common" "github.com/hajimehoshi/ebiten/exp/audio" - "image/color" - "log" - "math" ) const ( @@ -50,15 +53,26 @@ func init() { } var ( - noteLCache = map[int][]int16{} - noteRCache = map[int][]int16{} + noteCache = map[int][]byte{} ) +func toBytes(l, r []int16) []byte { + if len(l) != len(r) { + panic("len(l) must equal to len(r)") + } + b := &bytes.Buffer{} + for i := 0; i < len(l); i++ { + if err := binary.Write(b, binary.LittleEndian, []int16{l[i], r[i]}); err != nil { + panic(err) + } + } + return b.Bytes() +} + func addNote(freq float64, vol float64) { f := int(freq) - if l, ok := noteLCache[f]; ok { - r := noteRCache[f] - audio.Play(-1, l, r) + if n, ok := noteCache[f]; ok { + audio.Play(-1, n) return } length := len(pcm) * baseFreq / f @@ -73,9 +87,9 @@ func addNote(freq float64, vol float64) { jj += f j = jj / baseFreq } - noteLCache[f] = l - noteRCache[f] = r - audio.Play(-1, l, r) + n := toBytes(l, r) + noteCache[f] = n + audio.Play(-1, n) } var keys = []ebiten.Key{ diff --git a/exp/audio/audio.go b/exp/audio/audio.go index 750b57b09..a7b3f0f36 100644 --- a/exp/audio/audio.go +++ b/exp/audio/audio.go @@ -15,6 +15,9 @@ package audio import ( + "bytes" + "encoding/binary" + "github.com/hajimehoshi/ebiten/exp/audio/internal" ) @@ -26,13 +29,32 @@ func SampleRate() int { // MaxChannel is a max number of channels. var MaxChannel = internal.MaxChannel +func toLR(data []byte) ([]int16, []int16) { + buf := bytes.NewReader(data) + b := make([]int16, len(data)/2) + if err := binary.Read(buf, binary.LittleEndian, b); err != nil { + panic(err) + } + l := make([]int16, len(data)/4) + r := make([]int16, len(data)/4) + for i := 0; i < len(data)/4; i++ { + l[i] = b[2*i] + r[i] = b[2*i+1] + } + return l, r +} + // Play appends the given data to the given channel. // // channel must be -1 or a channel index. If channel is -1, an empty channel is automatically selected. // If the channel is not empty, this function does nothing and returns false. This returns true otherwise. // +// data's format must be linear PCM (44100Hz, stereo, 16bit little endian). +// // This function is useful to play SE or a note of PCM synthesis immediately. -func Play(channel int, l []int16, r []int16) bool { +func Play(channel int, data []byte) bool { + l, r := toLR(data) + // TODO: Pass data directly. return internal.Play(channel, l, r) } @@ -41,8 +63,12 @@ func Play(channel int, l []int16, r []int16) bool { // // channel must be a channel index. You can't give -1 to channel. // +// data's format must be linear PCM (44100Hz, stereo, 16bit little endian). +// // This function is useful to play streaming data. -func Queue(channel int, l []int16, r []int16) { +func Queue(channel int, data []byte) { + l, r := toLR(data) + // TODO: Pass data directly. internal.Queue(channel, l, r) }