audio: Use IO as source

This commit is contained in:
Hajime Hoshi 2016-02-11 02:04:23 +09:00
parent c6a431c9ab
commit d52118639d
6 changed files with 62 additions and 31 deletions

View File

@ -15,6 +15,7 @@
package main
import (
"bytes"
"fmt"
"log"
"math"
@ -85,6 +86,13 @@ func toBytes(l, r []int16) []byte {
return b
}
type stream struct {
*bytes.Reader
}
func (s *stream) Close() error {
return nil
}
func addNote() {
size := sampleRate / 60
notes := []float64{freqC, freqD, freqE, freqF, freqG, freqA * 2, freqB * 2}
@ -113,7 +121,8 @@ func addNote() {
vol := 1.0 / 16.0
square(l, vol, freq, 0.25)
square(r, vol, freq, 0.25)
audio.Queue(toBytes(l, r), sampleRate)
b := toBytes(l, r)
audio.Queue(&stream{bytes.NewReader(b)}, sampleRate)
}
func update(screen *ebiten.Image) error {

View File

@ -15,6 +15,7 @@
package main
import (
"bytes"
"fmt"
"image/color"
"log"
@ -69,10 +70,18 @@ func toBytes(l, r []int16) []byte {
return b
}
type stream struct {
*bytes.Reader
}
func (s *stream) Close() error {
return nil
}
func addNote(freq float64, vol float64) {
f := int(freq)
if n, ok := noteCache[f]; ok {
audio.Queue(n, sampleRate)
audio.Queue(&stream{bytes.NewReader(n)}, sampleRate)
return
}
length := len(pcm) * baseFreq / f
@ -89,7 +98,7 @@ func addNote(freq float64, vol float64) {
}
n := toBytes(l, r)
noteCache[f] = n
audio.Queue(n, sampleRate)
audio.Queue(&stream{bytes.NewReader(n)}, sampleRate)
}
var keys = []ebiten.Key{

View File

@ -15,19 +15,24 @@
package audio
import (
"io"
"github.com/hajimehoshi/ebiten/internal/audio"
)
type ReadSeekCloser interface {
io.ReadSeeker
io.Closer
}
// Queue queues the given data to the given channel.
// The given data is queued to the end of the buffer.
// This may not be played immediately when data already exists in the buffer.
//
// data's format must be linear PCM (16bits, 2 channel stereo, little endian)
// src's format must be linear PCM (16bits, 2 channel stereo, little endian)
// without a header (e.g. RIFF header).
//
// TODO: Pass sample rate and num of channels.
func Queue(data []byte, sampleRate int) error {
return audio.Queue(data, sampleRate)
func Queue(src ReadSeekCloser, sampleRate int) error {
return audio.Queue(src, sampleRate)
}
// TODO: Add Clear function

View File

@ -14,16 +14,26 @@
package audio
import (
"io"
)
var audioEnabled = false
type ReadSeekCloser interface {
io.ReadSeeker
io.Closer
}
type chunk struct {
buffer []byte
buffer ReadSeekCloser
sampleRate int
}
func Init() {
initialize()
}
func Queue(data []byte, sampleRate int) error {
return playChunk(data, sampleRate)
func Queue(src ReadSeekCloser, sampleRate int) error {
return playChunk(src, sampleRate)
}

View File

@ -17,13 +17,15 @@
package audio
import (
"io/ioutil"
"github.com/gopherjs/gopherjs/js"
)
var context *js.Object
type audioProcessor struct {
data []byte
src ReadSeekCloser
sampleRate int
position float64
}
@ -38,9 +40,14 @@ func toLR(data []byte) ([]int16, []int16) {
return l, r
}
func (a *audioProcessor) playChunk(buf []byte) {
func (a *audioProcessor) play() error {
// TODO: Reading all data at once is temporary implemntation. Treat this as stream.
buf, err := ioutil.ReadAll(a.src)
if err != nil {
return err
}
if len(buf) == 0 {
return
return nil
}
const channelNum = 2
const bytesPerSample = channelNum * 16 / 8
@ -58,17 +65,16 @@ func (a *audioProcessor) playChunk(buf []byte) {
s.Call("connect", context.Get("destination"))
s.Call("start", a.position)
a.position += b.Get("duration").Float()
return nil
}
// TOOD: Implement IO version
func playChunk(data []byte, sampleRate int) error {
func playChunk(src ReadSeekCloser, sampleRate int) error {
a := &audioProcessor{
data: data,
src: src,
sampleRate: sampleRate,
position: context.Get("currentTime").Float(),
}
a.playChunk(data)
return nil
return a.play()
}
func initialize() {

View File

@ -17,25 +17,17 @@
package audio
import (
"bytes"
"time"
"golang.org/x/mobile/exp/audio"
)
type src struct {
*bytes.Reader
}
func (s *src) Close() error {
return nil
}
var players = map[*audio.Player]struct{}{}
func playChunk(data []byte, sampleRate int) error {
s := &src{bytes.NewReader(data)}
p, err := audio.NewPlayer(s, audio.Stereo16, int64(sampleRate))
func playChunk(src ReadSeekCloser, sampleRate int) error {
// TODO: audio.NewPlayer interprets WAV header, which we don't want.
// Use OpenAL or native API instead.
p, err := audio.NewPlayer(src, audio.Stereo16, int64(sampleRate))
if err != nil {
return err
}