From 13f6549cb65105b746ee4a86e0f671ad865aa95c Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Tue, 15 Jan 2019 01:07:21 +0900 Subject: [PATCH] audio/vorbis: Remove duplicated decoded data There was duplicated decoded date in audio/vorbis package and Ogg decoder package. This change removes this duplication and reduces memory usage. --- audio/vorbis/internal/stb/decode.go | 56 +++++++++++++++++++------ audio/vorbis/vorbis.go | 63 +++++++++-------------------- audio/vorbis/vorbis_js.go | 4 ++ 3 files changed, 65 insertions(+), 58 deletions(-) diff --git a/audio/vorbis/internal/stb/decode.go b/audio/vorbis/internal/stb/decode.go index c2aae6080..9b54cd43f 100644 --- a/audio/vorbis/internal/stb/decode.go +++ b/audio/vorbis/internal/stb/decode.go @@ -39,30 +39,55 @@ func init() { } type Samples struct { - samples [][]float32 - length int64 + samples [][]float32 + channels int + lengthInSamples int64 + posInSamples int64 } func (s *Samples) Read(buf []float32) (int, error) { - if len(s.samples) == 0 { + if s.posInSamples == s.lengthInSamples { return 0, io.EOF } - n := copy(buf, s.samples[0]) - s.samples[0] = s.samples[0][n:] - if len(s.samples[0]) == 0 { - s.samples = s.samples[1:] + if len(buf) == 0 { + return 0, nil } + + var p int64 + idx := 0 + for idx < len(s.samples) { + l := int64(len(s.samples[idx])) / int64(s.channels) + if p+l > s.posInSamples { + break + } + p += l + idx++ + } + start := (s.posInSamples - p) * int64(s.channels) + if start == int64(len(s.samples[idx])) { + idx++ + start = 0 + } + if len(s.samples[idx]) == 0 { + panic("not reached") + } + n := copy(buf, s.samples[idx][start:]) + s.posInSamples += int64(n) / int64(s.channels) return n, nil } func (s *Samples) Length() int64 { - return s.length + return s.lengthInSamples +} + +func (s *Samples) SetPosition(pos int64) error { + s.posInSamples = pos + return nil } func DecodeVorbis(buf []byte) (*Samples, int, int, error) { ch := make(chan error) samples := &Samples{} - channels := 0 sampleRate := 0 var f js.Callback @@ -82,21 +107,26 @@ func DecodeVorbis(buf []byte) (*Samples, int, int, error) { return } - if channels == 0 { - channels = r.Get("data").Length() + if samples.channels == 0 { + samples.channels = r.Get("data").Length() + } if sampleRate == 0 { sampleRate = r.Get("sampleRate").Int() } flattened := flatten.Invoke(r.Get("data")) + if flattened.Length() == 0 { + return + } + s := make([]float32, flattened.Length()) arr := js.TypedArrayOf(s) arr.Call("set", flattened) arr.Release() samples.samples = append(samples.samples, s) - samples.length += int64(len(s)) / int64(channels) + samples.lengthInSamples += int64(len(s)) / int64(samples.channels) }) arr := js.TypedArrayOf(buf) @@ -107,5 +137,5 @@ func DecodeVorbis(buf []byte) (*Samples, int, int, error) { return nil, 0, 0, err } - return samples, channels, sampleRate, nil + return samples, samples.channels, sampleRate, nil } diff --git a/audio/vorbis/vorbis.go b/audio/vorbis/vorbis.go index 57bf702cd..95cf94482 100644 --- a/audio/vorbis/vorbis.go +++ b/audio/vorbis/vorbis.go @@ -63,13 +63,13 @@ func (s *Stream) Size() int64 { type decoder interface { Read([]float32) (int, error) + SetPosition(int64) error Length() int64 Channels() int SampleRate() int } type decoded struct { - data []float32 totalBytes int readBytes int posInBytes int @@ -77,36 +77,6 @@ type decoded struct { decoder decoder } -func (d *decoded) readUntil(posInBytes int) error { - if d.decoder == nil { - return nil - } - - buffer := make([]float32, 8192) - for d.readBytes < posInBytes { - n, err := d.decoder.Read(buffer) - if n > 0 { - // Actual read bytes might exceed the total bytes. - if d.readBytes+n*2 > d.totalBytes { - n = (d.totalBytes - d.readBytes) / 2 - } - p := d.readBytes / 2 - for i := 0; i < n; i++ { - d.data[p+i] = buffer[i] - } - d.readBytes += n * 2 - } - if err == io.EOF { - d.decoder = nil - break - } - if err != nil { - return err - } - } - return nil -} - func (d *decoded) Read(b []byte) (int, error) { l := d.totalBytes - d.posInBytes if l > len(b) { @@ -115,22 +85,29 @@ func (d *decoded) Read(b []byte) (int, error) { if l < 0 { return 0, io.EOF } - // l must be even so that d.posInBytes is always even. - l = l / 2 * 2 - if err := d.readUntil(d.posInBytes + l); err != nil { + + bf := make([]float32, l/2) +retry: + n, err := d.decoder.Read(bf) + if err != nil && err != io.EOF { return 0, err } - for i := 0; i < l/2; i++ { - f := d.data[d.posInBytes/2+i] + if n == 0 && len(bf) > 0 && err != io.EOF { + // When l is too small, decoder's Read might return 0 for a while. Let's retry. + goto retry + } + + for i := 0; i < n; i++ { + f := bf[i] s := int16(f * (1<<15 - 1)) b[2*i] = uint8(s) b[2*i+1] = uint8(s >> 8) } - d.posInBytes += l - if d.posInBytes == d.totalBytes { - return l, io.EOF + d.posInBytes += 2 * n + if d.posInBytes == d.totalBytes || err == io.EOF { + return 2 * n, io.EOF } - return l, nil + return 2 * n, nil } func (d *decoded) Seek(offset int64, whence int) (int64, error) { @@ -146,9 +123,7 @@ func (d *decoded) Seek(offset int64, whence int) (int64, error) { // pos should be always even next = next / 2 * 2 d.posInBytes = int(next) - if err := d.readUntil(d.posInBytes); err != nil { - return 0, err - } + d.decoder.SetPosition(next / int64(d.decoder.Channels()) / 2) return next, nil } @@ -172,8 +147,6 @@ func decode(in audio.ReadSeekCloser) (*decoded, int, int, error) { return nil, 0, 0, err } d := &decoded{ - data: make([]float32, int(r.Length())*r.Channels()), - // TODO: r.Length() returns 0 when the format is unknown. // Should we check that? totalBytes: int(r.Length()) * r.Channels() * 2, // 2 means 16bit per sample. diff --git a/audio/vorbis/vorbis_js.go b/audio/vorbis/vorbis_js.go index 704167b7b..d18d66161 100644 --- a/audio/vorbis/vorbis_js.go +++ b/audio/vorbis/vorbis_js.go @@ -46,6 +46,10 @@ func (d *decoderImpl) SampleRate() int { return d.sampleRate } +func (d *decoderImpl) SetPosition(pos int64) error { + return d.samples.SetPosition(pos) +} + func newDecoder(in audio.ReadSeekCloser) (decoder, error) { buf, err := ioutil.ReadAll(in) if err != nil {