mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-11-13 22:47:26 +01:00
audio, audio/mp3, audio/vorbis, audio/wav: Remove Close functions
Fixes #859
This commit is contained in:
parent
946cf1d250
commit
f1f7b350de
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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()
|
|
||||||
}
|
|
||||||
|
@ -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()
|
|
||||||
}
|
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user