mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-11-10 04:57:26 +01:00
audio: Use IO as source
This commit is contained in:
parent
c6a431c9ab
commit
d52118639d
@ -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 {
|
||||
|
@ -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{
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user