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.
This commit is contained in:
Hajime Hoshi 2019-01-15 01:07:21 +09:00
parent 5cd66b6d6b
commit 13f6549cb6
3 changed files with 65 additions and 58 deletions

View File

@ -40,29 +40,54 @@ func init() {
type Samples struct { type Samples struct {
samples [][]float32 samples [][]float32
length int64 channels int
lengthInSamples int64
posInSamples int64
} }
func (s *Samples) Read(buf []float32) (int, error) { func (s *Samples) Read(buf []float32) (int, error) {
if len(s.samples) == 0 { if s.posInSamples == s.lengthInSamples {
return 0, io.EOF return 0, io.EOF
} }
n := copy(buf, s.samples[0]) if len(buf) == 0 {
s.samples[0] = s.samples[0][n:] return 0, nil
if len(s.samples[0]) == 0 {
s.samples = s.samples[1:]
} }
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 return n, nil
} }
func (s *Samples) Length() int64 { 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) { func DecodeVorbis(buf []byte) (*Samples, int, int, error) {
ch := make(chan error) ch := make(chan error)
samples := &Samples{} samples := &Samples{}
channels := 0
sampleRate := 0 sampleRate := 0
var f js.Callback var f js.Callback
@ -82,21 +107,26 @@ func DecodeVorbis(buf []byte) (*Samples, int, int, error) {
return return
} }
if channels == 0 { if samples.channels == 0 {
channels = r.Get("data").Length() samples.channels = r.Get("data").Length()
} }
if sampleRate == 0 { if sampleRate == 0 {
sampleRate = r.Get("sampleRate").Int() sampleRate = r.Get("sampleRate").Int()
} }
flattened := flatten.Invoke(r.Get("data")) flattened := flatten.Invoke(r.Get("data"))
if flattened.Length() == 0 {
return
}
s := make([]float32, flattened.Length()) s := make([]float32, flattened.Length())
arr := js.TypedArrayOf(s) arr := js.TypedArrayOf(s)
arr.Call("set", flattened) arr.Call("set", flattened)
arr.Release() arr.Release()
samples.samples = append(samples.samples, s) 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) arr := js.TypedArrayOf(buf)
@ -107,5 +137,5 @@ func DecodeVorbis(buf []byte) (*Samples, int, int, error) {
return nil, 0, 0, err return nil, 0, 0, err
} }
return samples, channels, sampleRate, nil return samples, samples.channels, sampleRate, nil
} }

View File

@ -63,13 +63,13 @@ func (s *Stream) Size() int64 {
type decoder interface { type decoder interface {
Read([]float32) (int, error) Read([]float32) (int, error)
SetPosition(int64) error
Length() int64 Length() int64
Channels() int Channels() int
SampleRate() int SampleRate() int
} }
type decoded struct { type decoded struct {
data []float32
totalBytes int totalBytes int
readBytes int readBytes int
posInBytes int posInBytes int
@ -77,36 +77,6 @@ type decoded struct {
decoder decoder 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) { func (d *decoded) Read(b []byte) (int, error) {
l := d.totalBytes - d.posInBytes l := d.totalBytes - d.posInBytes
if l > len(b) { if l > len(b) {
@ -115,22 +85,29 @@ func (d *decoded) Read(b []byte) (int, error) {
if l < 0 { if l < 0 {
return 0, io.EOF return 0, io.EOF
} }
// l must be even so that d.posInBytes is always even.
l = l / 2 * 2 bf := make([]float32, l/2)
if err := d.readUntil(d.posInBytes + l); err != nil { retry:
n, err := d.decoder.Read(bf)
if err != nil && err != io.EOF {
return 0, err return 0, err
} }
for i := 0; i < l/2; i++ { if n == 0 && len(bf) > 0 && err != io.EOF {
f := d.data[d.posInBytes/2+i] // 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)) s := int16(f * (1<<15 - 1))
b[2*i] = uint8(s) b[2*i] = uint8(s)
b[2*i+1] = uint8(s >> 8) b[2*i+1] = uint8(s >> 8)
} }
d.posInBytes += l d.posInBytes += 2 * n
if d.posInBytes == d.totalBytes { if d.posInBytes == d.totalBytes || err == io.EOF {
return l, io.EOF return 2 * n, io.EOF
} }
return l, nil return 2 * n, nil
} }
func (d *decoded) Seek(offset int64, whence int) (int64, error) { 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 // pos should be always even
next = next / 2 * 2 next = next / 2 * 2
d.posInBytes = int(next) d.posInBytes = int(next)
if err := d.readUntil(d.posInBytes); err != nil { d.decoder.SetPosition(next / int64(d.decoder.Channels()) / 2)
return 0, err
}
return next, nil return next, nil
} }
@ -172,8 +147,6 @@ func decode(in audio.ReadSeekCloser) (*decoded, int, int, error) {
return nil, 0, 0, err return nil, 0, 0, err
} }
d := &decoded{ d := &decoded{
data: make([]float32, int(r.Length())*r.Channels()),
// TODO: r.Length() returns 0 when the format is unknown. // TODO: r.Length() returns 0 when the format is unknown.
// 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.

View File

@ -46,6 +46,10 @@ func (d *decoderImpl) SampleRate() int {
return d.sampleRate return d.sampleRate
} }
func (d *decoderImpl) SetPosition(pos int64) error {
return d.samples.SetPosition(pos)
}
func newDecoder(in audio.ReadSeekCloser) (decoder, error) { func newDecoder(in audio.ReadSeekCloser) (decoder, error) {
buf, err := ioutil.ReadAll(in) buf, err := ioutil.ReadAll(in)
if err != nil { if err != nil {