audio, audio/mp3, audio/vorbis, audio/wav: Remove Close functions

Fixes #859
This commit is contained in:
Hajime Hoshi 2020-10-07 23:09:02 +09:00
parent 946cf1d250
commit f1f7b350de
15 changed files with 58 additions and 139 deletions

View File

@ -162,7 +162,7 @@ func (c *Context) addPlayer(p *playerImpl) {
c.players[p] = struct{}{} c.players[p] = struct{}{}
// Check the source duplication // Check the source duplication
srcs := map[io.ReadCloser]struct{}{} srcs := map[io.Reader]struct{}{}
for p := range c.players { for p := range c.players {
if _, ok := srcs[p.src]; ok { if _, ok := srcs[p.src]; ok {
c.err = errors.New("audio: a same source is used by multiple Player") c.err = errors.New("audio: a same source is used by multiple Player")
@ -211,34 +211,6 @@ func (c *Context) SampleRate() int {
return c.sampleRate return c.sampleRate
} }
// ReadSeekCloser is an io.ReadSeeker and io.Closer.
type ReadSeekCloser interface {
io.ReadSeeker
io.Closer
}
type bytesReadSeekCloser struct {
reader *bytes.Reader
}
func (b *bytesReadSeekCloser) Read(buf []byte) (int, error) {
return b.reader.Read(buf)
}
func (b *bytesReadSeekCloser) Seek(offset int64, whence int) (int64, error) {
return b.reader.Seek(offset, whence)
}
func (b *bytesReadSeekCloser) Close() error {
b.reader = nil
return nil
}
// BytesReadSeekCloser creates ReadSeekCloser from bytes.
func BytesReadSeekCloser(b []byte) ReadSeekCloser {
return &bytesReadSeekCloser{reader: bytes.NewReader(b)}
}
// Player is an audio player which has one stream. // Player is an audio player which has one stream.
// //
// Even when all references to a Player object is gone, // Even when all references to a Player object is gone,
@ -251,7 +223,7 @@ type Player struct {
type playerImpl struct { type playerImpl struct {
context *Context context *Context
src io.ReadCloser src io.Reader
sampleRate int sampleRate int
playing bool playing bool
closedExplicitly bool closedExplicitly bool
@ -278,8 +250,9 @@ type playerImpl struct {
// NewPlayer tries to call Seek of src to get the current position. // NewPlayer tries to call Seek of src to get the current position.
// NewPlayer returns error when the Seek returns error. // NewPlayer returns error when the Seek returns error.
// //
// NewPlayer takes the ownership of src. Player's Close calls src's Close. // A Player doesn't close src even if src implements io.Closer.
func NewPlayer(context *Context, src io.ReadCloser) (*Player, error) { // Closing the source is src owner's responsibility.
func NewPlayer(context *Context, src io.Reader) (*Player, error) {
p := &Player{ p := &Player{
&playerImpl{ &playerImpl{
context: context, context: context,
@ -308,7 +281,7 @@ func NewPlayer(context *Context, src io.ReadCloser) (*Player, error) {
// //
// The format of src should be same as noted at NewPlayer. // The format of src should be same as noted at NewPlayer.
func NewPlayerFromBytes(context *Context, src []byte) *Player { func NewPlayerFromBytes(context *Context, src []byte) *Player {
b := BytesReadSeekCloser(src) b := bytes.NewReader(src)
p, err := NewPlayer(context, b) p, err := NewPlayer(context, b)
if err != nil { if err != nil {
// Errors should never happen. // Errors should never happen.
@ -329,7 +302,7 @@ func (p *Player) finalize() {
// When closing, the stream owned by the player will also be closed by calling its Close. // When closing, the stream owned by the player will also be closed by calling its Close.
// This means that the source stream passed via NewPlayer will also be closed. // This means that the source stream passed via NewPlayer will also be closed.
// //
// Close returns error when closing the source returns error. // Close returns error when the player is already closed.
func (p *Player) Close() error { func (p *Player) Close() error {
runtime.SetFinalizer(p, nil) runtime.SetFinalizer(p, nil)
return p.p.Close() return p.p.Close()
@ -344,11 +317,6 @@ func (p *playerImpl) Close() error {
return fmt.Errorf("audio: the player is already closed") return fmt.Errorf("audio: the player is already closed")
} }
p.closedExplicitly = true p.closedExplicitly = true
// src.Close is called only when Player's Close is called.
// TODO: Is it ok not to call src.Close when GCed?
if err := p.src.Close(); err != nil {
return err
}
return nil return nil
} }

View File

@ -15,6 +15,7 @@
package audio_test package audio_test
import ( import (
"bytes"
"runtime" "runtime"
"testing" "testing"
"time" "time"
@ -38,7 +39,7 @@ func TestGC(t *testing.T) {
setup() setup()
defer teardown() defer teardown()
p, _ := NewPlayer(context, BytesReadSeekCloser(make([]byte, 4))) p, _ := NewPlayer(context, bytes.NewReader(make([]byte, 4)))
got := PlayersNumForTesting() got := PlayersNumForTesting()
if want := 0; got != want { if want := 0; got != want {
t.Errorf("PlayersNum(): got: %d, want: %d", got, want) t.Errorf("PlayersNum(): got: %d, want: %d", got, want)
@ -74,7 +75,7 @@ func TestSameSourcePlayers(t *testing.T) {
setup() setup()
defer teardown() defer teardown()
src := BytesReadSeekCloser(make([]byte, 4)) src := bytes.NewReader(make([]byte, 4))
p0, err := NewPlayer(context, src) p0, err := NewPlayer(context, src)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -101,7 +102,7 @@ func TestPauseBeforeInit(t *testing.T) {
setup() setup()
defer teardown() defer teardown()
src := BytesReadSeekCloser(make([]byte, 4)) src := bytes.NewReader(make([]byte, 4))
p, err := NewPlayer(context, src) p, err := NewPlayer(context, src)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)

View File

@ -15,11 +15,11 @@
package convert_test package convert_test
import ( import (
"bytes"
"io/ioutil" "io/ioutil"
"math" "math"
"testing" "testing"
"github.com/hajimehoshi/ebiten/v2/audio"
. "github.com/hajimehoshi/ebiten/v2/audio/internal/convert" . "github.com/hajimehoshi/ebiten/v2/audio/internal/convert"
) )
@ -69,7 +69,7 @@ func TestResampling(t *testing.T) {
} }
for _, c := range cases { for _, c := range cases {
inB := newSoundBytes(c.In) inB := newSoundBytes(c.In)
outS := NewResampling(audio.BytesReadSeekCloser(inB), int64(len(inB)), c.In, c.Out) outS := NewResampling(bytes.NewReader(inB), int64(len(inB)), c.In, c.Out)
gotB, err := ioutil.ReadAll(outS) gotB, err := ioutil.ReadAll(outS)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)

View File

@ -16,17 +16,15 @@ package convert
import ( import (
"io" "io"
"github.com/hajimehoshi/ebiten/v2/audio"
) )
type Stereo16 struct { type Stereo16 struct {
source audio.ReadSeekCloser source io.ReadSeeker
mono bool mono bool
eight bool eight bool
} }
func NewStereo16(source audio.ReadSeekCloser, mono, eight bool) *Stereo16 { func NewStereo16(source io.ReadSeeker, mono, eight bool) *Stereo16 {
return &Stereo16{ return &Stereo16{
source: source, source: source,
mono: mono, mono: mono,
@ -91,7 +89,3 @@ func (s *Stereo16) Seek(offset int64, whence int) (int64, error) {
} }
return s.source.Seek(offset, whence) return s.source.Seek(offset, whence)
} }
func (s *Stereo16) Close() error {
return s.source.Close()
}

View File

@ -21,20 +21,20 @@ import (
// InfiniteLoop represents a looped stream which never ends. // InfiniteLoop represents a looped stream which never ends.
type InfiniteLoop struct { type InfiniteLoop struct {
src ReadSeekCloser src io.ReadSeeker
lstart int64 lstart int64
llength int64 llength int64
pos int64 pos int64
} }
// NewInfiniteLoop creates a new infinite loop stream with a source stream and length in bytes. // NewInfiniteLoop creates a new infinite loop stream with a source stream and length in bytes.
func NewInfiniteLoop(src ReadSeekCloser, length int64) *InfiniteLoop { func NewInfiniteLoop(src io.ReadSeeker, length int64) *InfiniteLoop {
return NewInfiniteLoopWithIntro(src, 0, length) return NewInfiniteLoopWithIntro(src, 0, length)
} }
// NewInfiniteLoopWithIntro creates a new infinite loop stream with an intro part. // NewInfiniteLoopWithIntro creates a new infinite loop stream with an intro part.
// NewInfiniteLoopWithIntro accepts a source stream src, introLength in bytes and loopLength in bytes. // NewInfiniteLoopWithIntro accepts a source stream src, introLength in bytes and loopLength in bytes.
func NewInfiniteLoopWithIntro(src ReadSeekCloser, introLength int64, loopLength int64) *InfiniteLoop { func NewInfiniteLoopWithIntro(src io.ReadSeeker, introLength int64, loopLength int64) *InfiniteLoop {
return &InfiniteLoop{ return &InfiniteLoop{
src: src, src: src,
lstart: introLength, lstart: introLength,
@ -121,8 +121,3 @@ func (i *InfiniteLoop) Seek(offset int64, whence int) (int64, error) {
i.pos = next i.pos = next
return i.pos, nil return i.pos, nil
} }
// Close is implementation of ReadSeekCloser's Close.
func (l *InfiniteLoop) Close() error {
return l.src.Close()
}

View File

@ -15,6 +15,7 @@
package audio_test package audio_test
import ( import (
"bytes"
"io" "io"
"math" "math"
"testing" "testing"
@ -31,7 +32,7 @@ func TestInfiniteLoop(t *testing.T) {
for i := range src { for i := range src {
src[i] = indexToByte(i) src[i] = indexToByte(i)
} }
l := NewInfiniteLoop(BytesReadSeekCloser(src), int64(len(src))) l := NewInfiniteLoop(bytes.NewReader(src), int64(len(src)))
buf := make([]byte, len(src)*4) buf := make([]byte, len(src)*4)
if _, err := io.ReadFull(l, buf); err != nil { if _, err := io.ReadFull(l, buf); err != nil {
@ -93,7 +94,7 @@ func TestInfiniteLoopWithIntro(t *testing.T) {
for i := range src { for i := range src {
src[i] = indexToByte(i) src[i] = indexToByte(i)
} }
srcInf := NewInfiniteLoop(BytesReadSeekCloser(src), srcLength) srcInf := NewInfiniteLoop(bytes.NewReader(src), srcLength)
l := NewInfiniteLoopWithIntro(srcInf, introLength, loopLength) l := NewInfiniteLoopWithIntro(srcInf, introLength, loopLength)
buf := make([]byte, srcLength*4) buf := make([]byte, srcLength*4)

View File

@ -20,7 +20,6 @@ package mp3
import ( import (
"io" "io"
"runtime"
"github.com/hajimehoshi/go-mp3" "github.com/hajimehoshi/go-mp3"
@ -32,7 +31,6 @@ import (
type Stream struct { type Stream struct {
orig *mp3.Decoder orig *mp3.Decoder
resampling *convert.Resampling resampling *convert.Resampling
toClose io.Closer
} }
// Read is implementation of io.Reader's Read. // Read is implementation of io.Reader's Read.
@ -51,12 +49,6 @@ func (s *Stream) Seek(offset int64, whence int) (int64, error) {
return s.orig.Seek(offset, whence) return s.orig.Seek(offset, whence)
} }
// Close is implementation of io.Closer's Close.
func (s *Stream) Close() error {
runtime.SetFinalizer(s, nil)
return s.toClose.Close()
}
// Length returns the size of decoded stream in bytes. // Length returns the size of decoded stream in bytes.
func (s *Stream) Length() int64 { func (s *Stream) Length() int64 {
if s.resampling != nil { if s.resampling != nil {
@ -71,8 +63,9 @@ func (s *Stream) Length() int64 {
// //
// Decode automatically resamples the stream to fit with the audio context if necessary. // Decode automatically resamples the stream to fit with the audio context if necessary.
// //
// Decode takes the ownership of src, and Stream's Close function closes src. // A Stream doesn't close src even if src implements io.Closer.
func Decode(context *audio.Context, src audio.ReadSeekCloser) (*Stream, error) { // Closing the source is src owner's responsibility.
func Decode(context *audio.Context, src io.ReadSeeker) (*Stream, error) {
d, err := mp3.NewDecoder(src) d, err := mp3.NewDecoder(src)
if err != nil { if err != nil {
return nil, err return nil, err
@ -85,8 +78,6 @@ func Decode(context *audio.Context, src audio.ReadSeekCloser) (*Stream, error) {
s := &Stream{ s := &Stream{
orig: d, orig: d,
resampling: r, resampling: r,
toClose: src,
} }
runtime.SetFinalizer(s, (*Stream).Close)
return s, nil return s, nil
} }

View File

@ -18,7 +18,6 @@ package vorbis
import ( import (
"fmt" "fmt"
"io" "io"
"runtime"
"github.com/jfreymuth/oggvorbis" "github.com/jfreymuth/oggvorbis"
@ -28,7 +27,7 @@ import (
// Stream is a decoded audio stream. // Stream is a decoded audio stream.
type Stream struct { type Stream struct {
decoded audio.ReadSeekCloser decoded io.ReadSeeker
size int64 size int64
} }
@ -44,15 +43,6 @@ func (s *Stream) Seek(offset int64, whence int) (int64, error) {
return s.decoded.Seek(offset, whence) return s.decoded.Seek(offset, whence)
} }
// Close is implementation of io.Closer's Close.
func (s *Stream) Close() error {
runtime.SetFinalizer(s, nil)
if err := s.decoded.Close(); err != nil {
return err
}
return nil
}
// Length returns the size of decoded stream in bytes. // Length returns the size of decoded stream in bytes.
func (s *Stream) Length() int64 { func (s *Stream) Length() int64 {
return s.size return s.size
@ -69,7 +59,6 @@ type decoder interface {
type decoded struct { type decoded struct {
totalBytes int totalBytes int
posInBytes int posInBytes int
source io.Closer
decoder decoder decoder decoder
decoderr io.Reader decoderr io.Reader
} }
@ -122,22 +111,12 @@ func (d *decoded) Seek(offset int64, whence int) (int64, error) {
return next, nil return next, nil
} }
func (d *decoded) Close() error {
runtime.SetFinalizer(d, nil)
if err := d.source.Close(); err != nil {
return err
}
d.decoder = nil
d.decoderr = nil
return nil
}
func (d *decoded) Length() int64 { func (d *decoded) Length() int64 {
return int64(d.totalBytes) return int64(d.totalBytes)
} }
// decode accepts an ogg stream and returns a decorded stream. // decode accepts an ogg stream and returns a decorded stream.
func decode(in audio.ReadSeekCloser) (*decoded, int, int, error) { func decode(in io.ReadSeeker) (*decoded, int, int, error) {
r, err := oggvorbis.NewReader(in) r, err := oggvorbis.NewReader(in)
if err != nil { if err != nil {
return nil, 0, 0, err return nil, 0, 0, err
@ -147,10 +126,8 @@ func decode(in audio.ReadSeekCloser) (*decoded, int, int, error) {
// Should we check that? // Should we check that?
totalBytes: int(r.Length()) * r.Channels() * 2, // 2 means 16bit per sample. totalBytes: int(r.Length()) * r.Channels() * 2, // 2 means 16bit per sample.
posInBytes: 0, posInBytes: 0,
source: in,
decoder: r, decoder: r,
} }
runtime.SetFinalizer(d, (*decoded).Close)
if _, err := d.Read(make([]byte, 65536)); err != nil && err != io.EOF { if _, err := d.Read(make([]byte, 65536)); err != nil && err != io.EOF {
return nil, 0, 0, err return nil, 0, 0, err
} }
@ -166,8 +143,9 @@ func decode(in audio.ReadSeekCloser) (*decoded, int, int, error) {
// //
// Decode automatically resamples the stream to fit with the audio context if necessary. // Decode automatically resamples the stream to fit with the audio context if necessary.
// //
// Decode takes the ownership of src, and Stream's Close function closes src. // A Stream doesn't close src even if src implements io.Closer.
func Decode(context *audio.Context, src audio.ReadSeekCloser) (*Stream, error) { // Closing the source is src owner's responsibility.
func Decode(context *audio.Context, src io.ReadSeeker) (*Stream, error) {
decoded, channelNum, sampleRate, err := decode(src) decoded, channelNum, sampleRate, err := decode(src)
if err != nil { if err != nil {
return nil, err return nil, err
@ -175,7 +153,7 @@ func Decode(context *audio.Context, src audio.ReadSeekCloser) (*Stream, error) {
if channelNum != 1 && channelNum != 2 { if channelNum != 1 && channelNum != 2 {
return nil, fmt.Errorf("vorbis: number of channels must be 1 or 2 but was %d", channelNum) return nil, fmt.Errorf("vorbis: number of channels must be 1 or 2 but was %d", channelNum)
} }
var s audio.ReadSeekCloser = decoded var s io.ReadSeeker = decoded
size := decoded.Length() size := decoded.Length()
if channelNum == 1 { if channelNum == 1 {
s = convert.NewStereo16(s, true, false) s = convert.NewStereo16(s, true, false)
@ -187,6 +165,5 @@ func Decode(context *audio.Context, src audio.ReadSeekCloser) (*Stream, error) {
size = r.Length() size = r.Length()
} }
stream := &Stream{decoded: s, size: size} stream := &Stream{decoded: s, size: size}
runtime.SetFinalizer(stream, (*Stream).Close)
return stream, nil return stream, nil
} }

View File

@ -15,6 +15,7 @@
package vorbis_test package vorbis_test
import ( import (
"bytes"
"testing" "testing"
"github.com/jfreymuth/oggvorbis" "github.com/jfreymuth/oggvorbis"
@ -28,12 +29,12 @@ var audioContext = audio.NewContext(44100)
func TestMono(t *testing.T) { func TestMono(t *testing.T) {
bs := test_mono_ogg bs := test_mono_ogg
s, err := Decode(audioContext, audio.BytesReadSeekCloser(bs)) s, err := Decode(audioContext, bytes.NewReader(bs))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
r, err := oggvorbis.NewReader(audio.BytesReadSeekCloser(bs)) r, err := oggvorbis.NewReader(bytes.NewReader(bs))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -54,7 +55,7 @@ func TestMono(t *testing.T) {
func TestTooShort(t *testing.T) { func TestTooShort(t *testing.T) {
bs := test_tooshort_ogg bs := test_tooshort_ogg
s, err := Decode(audioContext, audio.BytesReadSeekCloser(bs)) s, err := Decode(audioContext, bytes.NewReader(bs))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@ -19,7 +19,6 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"io" "io"
"runtime"
"github.com/hajimehoshi/ebiten/v2/audio" "github.com/hajimehoshi/ebiten/v2/audio"
"github.com/hajimehoshi/ebiten/v2/audio/internal/convert" "github.com/hajimehoshi/ebiten/v2/audio/internal/convert"
@ -27,7 +26,7 @@ import (
// Stream is a decoded audio stream. // Stream is a decoded audio stream.
type Stream struct { type Stream struct {
inner audio.ReadSeekCloser inner io.ReadSeeker
size int64 size int64
} }
@ -43,19 +42,13 @@ func (s *Stream) Seek(offset int64, whence int) (int64, error) {
return s.inner.Seek(offset, whence) return s.inner.Seek(offset, whence)
} }
// Read is implementation of io.Closer's Close.
func (s *Stream) Close() error {
runtime.SetFinalizer(s, nil)
return s.inner.Close()
}
// Length returns the size of decoded stream in bytes. // Length returns the size of decoded stream in bytes.
func (s *Stream) Length() int64 { func (s *Stream) Length() int64 {
return s.size return s.size
} }
type stream struct { type stream struct {
src audio.ReadSeekCloser src io.ReadSeeker
headerSize int64 headerSize int64
dataSize int64 dataSize int64
remaining int64 remaining int64
@ -100,12 +93,6 @@ func (s *stream) Seek(offset int64, whence int) (int64, error) {
return n - s.headerSize, nil return n - s.headerSize, nil
} }
// Close is implementation of io.Closer's Close.
func (s *stream) Close() error {
runtime.SetFinalizer(s, nil)
return s.src.Close()
}
// Decode decodes WAV (RIFF) data to playable stream. // Decode decodes WAV (RIFF) data to playable stream.
// //
// The format must be 1 or 2 channels, 8bit or 16bit little endian PCM. // The format must be 1 or 2 channels, 8bit or 16bit little endian PCM.
@ -115,8 +102,9 @@ func (s *stream) Close() error {
// //
// Decode automatically resamples the stream to fit with the audio context if necessary. // Decode automatically resamples the stream to fit with the audio context if necessary.
// //
// Decode takes the ownership of src, and Stream's Close function closes src. // A Stream doesn't close src even if src implements io.Closer.
func Decode(context *audio.Context, src audio.ReadSeekCloser) (*Stream, error) { // Closing the source is src owner's responsibility.
func Decode(context *audio.Context, src io.ReadSeeker) (*Stream, error) {
buf := make([]byte, 12) buf := make([]byte, 12)
n, err := io.ReadFull(src, buf) n, err := io.ReadFull(src, buf)
if n != len(buf) { if n != len(buf) {
@ -203,13 +191,12 @@ chunks:
headerSize += size headerSize += size
} }
} }
var s audio.ReadSeekCloser = &stream{ var s io.ReadSeeker = &stream{
src: src, src: src,
headerSize: headerSize, headerSize: headerSize,
dataSize: dataSize, dataSize: dataSize,
remaining: dataSize, remaining: dataSize,
} }
runtime.SetFinalizer(s, (*stream).Close)
if mono || bitsPerSample != 16 { if mono || bitsPerSample != 16 {
s = convert.NewStereo16(s, mono, bitsPerSample != 16) s = convert.NewStereo16(s, mono, bitsPerSample != 16)
@ -226,6 +213,5 @@ chunks:
dataSize = r.Length() dataSize = r.Length()
} }
ss := &Stream{inner: s, size: dataSize} ss := &Stream{inner: s, size: dataSize}
runtime.SetFinalizer(ss, (*Stream).Close)
return ss, nil return ss, nil
} }

View File

@ -20,8 +20,10 @@
package main package main
import ( import (
"bytes"
"fmt" "fmt"
"image/color" "image/color"
"io"
"io/ioutil" "io/ioutil"
"log" "log"
"time" "time"
@ -87,7 +89,7 @@ func playerBarRect() (x, y, w, h int) {
func NewPlayer(audioContext *audio.Context, musicType musicType) (*Player, error) { func NewPlayer(audioContext *audio.Context, musicType musicType) (*Player, error) {
type audioStream interface { type audioStream interface {
audio.ReadSeekCloser io.ReadSeeker
Length() int64 Length() int64
} }
@ -98,13 +100,13 @@ func NewPlayer(audioContext *audio.Context, musicType musicType) (*Player, error
switch musicType { switch musicType {
case typeOgg: case typeOgg:
var err error var err error
s, err = vorbis.Decode(audioContext, audio.BytesReadSeekCloser(raudio.Ragtime_ogg)) s, err = vorbis.Decode(audioContext, bytes.NewReader(raudio.Ragtime_ogg))
if err != nil { if err != nil {
return nil, err return nil, err
} }
case typeMP3: case typeMP3:
var err error var err error
s, err = mp3.Decode(audioContext, audio.BytesReadSeekCloser(raudio.Classic_mp3)) s, err = mp3.Decode(audioContext, bytes.NewReader(raudio.Classic_mp3))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -128,7 +130,7 @@ func NewPlayer(audioContext *audio.Context, musicType musicType) (*Player, error
} }
player.audioPlayer.Play() player.audioPlayer.Play()
go func() { go func() {
s, err := wav.Decode(audioContext, audio.BytesReadSeekCloser(raudio.Jab_wav)) s, err := wav.Decode(audioContext, bytes.NewReader(raudio.Jab_wav))
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
return return

View File

@ -17,6 +17,7 @@
package main package main
import ( import (
"bytes"
"fmt" "fmt"
"log" "log"
"time" "time"
@ -50,7 +51,7 @@ func (g *Game) Update() error {
// Decode an Ogg file. // Decode an Ogg file.
// oggS is a decoded io.ReadCloser and io.Seeker. // oggS is a decoded io.ReadCloser and io.Seeker.
oggS, err := vorbis.Decode(audioContext, audio.BytesReadSeekCloser(raudio.Ragtime_ogg)) oggS, err := vorbis.Decode(audioContext, bytes.NewReader(raudio.Ragtime_ogg))
if err != nil { if err != nil {
return err return err
} }

View File

@ -21,6 +21,7 @@ import (
"fmt" "fmt"
"image" "image"
_ "image/png" _ "image/png"
"io"
"log" "log"
"math" "math"
"time" "time"
@ -64,7 +65,7 @@ func (g *Game) initAudio() {
// Decode an Ogg file. // Decode an Ogg file.
// oggS is a decoded io.ReadCloser and io.Seeker. // oggS is a decoded io.ReadCloser and io.Seeker.
oggS, err := vorbis.Decode(audioContext, audio.BytesReadSeekCloser(raudio.Ragtime_ogg)) oggS, err := vorbis.Decode(audioContext, bytes.NewReader(raudio.Ragtime_ogg))
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
@ -145,12 +146,12 @@ func main() {
// StereoPanStream is an audio buffer that changes the stereo channel's signal // StereoPanStream is an audio buffer that changes the stereo channel's signal
// based on the Panning. // based on the Panning.
type StereoPanStream struct { type StereoPanStream struct {
audio.ReadSeekCloser io.ReadSeeker
pan float64 // -1: left; 0: center; 1: right pan float64 // -1: left; 0: center; 1: right
} }
func (s *StereoPanStream) Read(p []byte) (n int, err error) { func (s *StereoPanStream) Read(p []byte) (n int, err error) {
n, err = s.ReadSeekCloser.Read(p) n, err = s.ReadSeeker.Read(p)
if err != nil { if err != nil {
return return
} }
@ -187,8 +188,8 @@ func (s *StereoPanStream) Pan() float64 {
// The src's format must be linear PCM (16bits little endian, 2 channel stereo) // The src's format must be linear PCM (16bits little endian, 2 channel stereo)
// without a header (e.g. RIFF header). The sample rate must be same as that // without a header (e.g. RIFF header). The sample rate must be same as that
// of the audio context. // of the audio context.
func NewStereoPanStreamFromReader(src audio.ReadSeekCloser) *StereoPanStream { func NewStereoPanStreamFromReader(src io.ReadSeeker) *StereoPanStream {
return &StereoPanStream{ return &StereoPanStream{
ReadSeekCloser: src, ReadSeeker: src,
} }
} }

View File

@ -122,7 +122,7 @@ var (
) )
func init() { func init() {
jumpD, err := vorbis.Decode(audioContext, audio.BytesReadSeekCloser(raudio.Jump_ogg)) jumpD, err := vorbis.Decode(audioContext, bytes.NewReader(raudio.Jump_ogg))
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
@ -131,7 +131,7 @@ func init() {
log.Fatal(err) log.Fatal(err)
} }
jabD, err := wav.Decode(audioContext, audio.BytesReadSeekCloser(raudio.Jab_wav)) jabD, err := wav.Decode(audioContext, bytes.NewReader(raudio.Jab_wav))
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }

View File

@ -17,6 +17,7 @@
package main package main
import ( import (
"bytes"
"log" "log"
"github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2"
@ -59,7 +60,7 @@ func init() {
// ... // ...
// Decode wav-formatted data and retrieve decoded PCM stream. // Decode wav-formatted data and retrieve decoded PCM stream.
d, err := wav.Decode(g.audioContext, audio.BytesReadSeekCloser(raudio.Jab_wav)) d, err := wav.Decode(g.audioContext, bytes.NewReader(raudio.Jab_wav))
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }