diff --git a/audio/internal/convert/seeker.go b/audio/internal/convert/seeker.go new file mode 100644 index 000000000..5583c52cc --- /dev/null +++ b/audio/internal/convert/seeker.go @@ -0,0 +1,76 @@ +// Copyright 2017 The Ebiten Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package convert + +import ( + "io" + + "github.com/hajimehoshi/ebiten/audio" +) + +// A Seeker is io.Seeker and io.ReadSeekCloser. +type Seeker struct { + rc io.ReadCloser + data []uint8 + pos int64 + eof bool +} + +// Read is implementation of io.Reader's Read. +func (s *Seeker) Read(buf []byte) (int, error) { + for int64(len(s.data)) <= s.pos && !s.eof { + buf := make([]uint8, 4096) + n, err := s.rc.Read(buf) + s.data = append(s.data, buf[:n]...) + if err != nil { + if err == io.EOF { + s.eof = true + break + } + return 0, err + } + } + if int64(len(s.data)) <= s.pos && s.eof { + return 0, io.EOF + } + n := copy(buf, s.data[s.pos:]) + s.pos += int64(n) + return n, nil +} + +// Seek is implementation of io.Seeker's Seek. +func (s *Seeker) Seek(offset int64, whence int) (int64, error) { + switch whence { + case io.SeekStart: + s.pos = offset + case io.SeekCurrent: + s.pos += offset + case io.SeekEnd: + panic("not implemented") + } + return s.pos, nil +} + +// Read is implementation of io.Closer's Close. +func (s *Seeker) Close() error { + return s.rc.Close() +} + +// NewSeeker creates audio.ReadSeekCloser from io.ReadSeeker. +func NewSeeker(rc io.ReadCloser) audio.ReadSeekCloser { + return &Seeker{ + rc: rc, + } +} diff --git a/audio/mp3/decode.go b/audio/mp3/decode.go index b3f457b80..3dcb65051 100644 --- a/audio/mp3/decode.go +++ b/audio/mp3/decode.go @@ -18,61 +18,33 @@ package mp3 import ( - "io" - "github.com/hajimehoshi/ebiten/audio" + "github.com/hajimehoshi/ebiten/audio/internal/convert" ) type Stream struct { - inner *Decoder - data []uint8 - pos int64 - eof bool + inner audio.ReadSeekCloser + size int64 } // Read is implementation of io.Reader's Read. func (s *Stream) Read(buf []byte) (int, error) { - for int64(len(s.data)) <= s.pos && !s.eof { - buf := make([]uint8, 4096) - n, err := s.inner.Read(buf) - s.data = append(s.data, buf[:n]...) - if err != nil { - if err == io.EOF { - s.eof = true - break - } - return 0, err - } - } - if int64(len(s.data)) <= s.pos && s.eof { - return 0, io.EOF - } - n := copy(buf, s.data[s.pos:]) - s.pos += int64(n) - return n, nil + return s.inner.Read(buf) } // Seek is implementation of io.Seeker's Seek. func (s *Stream) Seek(offset int64, whence int) (int64, error) { - switch whence { - case io.SeekStart: - s.pos = offset - case io.SeekCurrent: - s.pos += offset - case io.SeekEnd: - panic("not implemented") - } - return s.pos, nil + return s.inner.Seek(offset, whence) } // Read is implementation of io.Closer's Close. func (s *Stream) Close() error { - return nil + return s.inner.Close() } // Size returns the size of decoded stream in bytes. func (s *Stream) Size() int64 { - return int64(s.inner.Length()) + return s.size } func Decode(context *audio.Context, src audio.ReadSeekCloser) (*Stream, error) { @@ -80,9 +52,15 @@ func Decode(context *audio.Context, src audio.ReadSeekCloser) (*Stream, error) { if err != nil { return nil, err } - s := &Stream{ - inner: d, - } // TODO: Resampling - return s, nil + var s audio.ReadSeekCloser = convert.NewSeeker(d) + size := d.Length() + if d.SampleRate() != context.SampleRate() { + s = convert.NewResampling(s, d.Length(), d.SampleRate(), context.SampleRate()) + size = size * int64(context.SampleRate()) / int64(d.SampleRate()) + } + return &Stream{ + inner: s, + size: size, + }, nil } diff --git a/audio/mp3/decode_notjs.go b/audio/mp3/decode_notjs.go index b0633663c..f24e87bbe 100644 --- a/audio/mp3/decode_notjs.go +++ b/audio/mp3/decode_notjs.go @@ -43,12 +43,16 @@ func (f *frame) decodeL3() []uint8 { } type source struct { - reader io.Reader + reader io.ReadCloser readerCache []uint8 readerPos int readerEOF bool } +func (s *source) Close() error { + return s.reader.Close() +} + func (s *source) rewind() error { seeker := s.reader.(io.Seeker) if _, err := seeker.Seek(0, io.SeekStart); err != nil { @@ -134,6 +138,11 @@ func (d *Decoder) Read(buf []uint8) (int, error) { return n, nil } +// Close is io.Closer's Close. +func (d *Decoder) Close() error { + return d.source.Close() +} + // SampleRate returns the sample rate like 44100. // // Note that the sample rate is retrieved from the first frame. @@ -149,7 +158,7 @@ func (d *Decoder) Length() int64 { return d.length } -func decode(r io.Reader) (*Decoder, error) { +func decode(r io.ReadCloser) (*Decoder, error) { s := &source{ reader: r, } diff --git a/examples/audio/main.go b/examples/audio/main.go index 7c0ec7fa5..ba2daef03 100644 --- a/examples/audio/main.go +++ b/examples/audio/main.go @@ -36,7 +36,7 @@ const ( // This sample rate doesn't match with wav/ogg's sample rate, // but decoders adjust them. - sampleRate = 44100 + sampleRate = 48000 ) var (