From f3647fc4515ea1ae75e6d2e2f5096d7be22793d6 Mon Sep 17 00:00:00 2001 From: faiface Date: Tue, 11 Jul 2017 20:00:29 +0200 Subject: [PATCH] audio: wav: add Seek and Position --- audio/wav/streamer.go | 107 ++++++++++++++++++++++++++---------------- 1 file changed, 67 insertions(+), 40 deletions(-) diff --git a/audio/wav/streamer.go b/audio/wav/streamer.go index 0cb5739..cbaa09a 100644 --- a/audio/wav/streamer.go +++ b/audio/wav/streamer.go @@ -7,115 +7,142 @@ import ( "github.com/pkg/errors" ) +type ReadSeekCloser interface { + io.Reader + io.Seeker + io.Closer +} + type Streamer struct { - rc io.ReadCloser + rsc ReadSeekCloser h header pos int32 err error } -func NewStreamer(rc io.ReadCloser) (*Streamer, error) { +func NewStreamer(rsc ReadSeekCloser) (s *Streamer, err error) { var ( - d Streamer - err error + d Streamer + herr error ) - d.rc = rc - d.h, err = readHeader(rc) - if err != nil { - rc.Close() - return nil, errors.Wrap(err, "wav") + d.rsc = rsc + defer func() { // hacky way to always close rsc if an error occured + if err != nil { + d.rsc.Close() + } + }() + d.h, herr = readHeader(rsc) + if herr != nil { + return nil, errors.Wrap(herr, "wav") } if d.h.formatType != 1 { - rc.Close() return nil, errors.New("wav: unsupported format type") } if d.h.numChans <= 0 { - rc.Close() return nil, errors.New("wav: invalid number of channels (less than 1)") } if d.h.bitsPerSample != 8 && d.h.bitsPerSample != 16 { - rc.Close() return nil, errors.New("wav: unsupported number of bits per sample, 8 or 16 are supported") } return &d, nil } -func (d *Streamer) Err() error { - return d.err +func (s *Streamer) Err() error { + return s.err } -func (d *Streamer) Duration() time.Duration { - numBytes := time.Duration(d.h.dataSize) - perFrame := time.Duration(d.h.bytesPerFrame) - sampRate := time.Duration(d.h.sampleRate) +func (s *Streamer) Duration() time.Duration { + numBytes := time.Duration(s.h.dataSize) + perFrame := time.Duration(s.h.bytesPerFrame) + sampRate := time.Duration(s.h.sampleRate) return numBytes / perFrame * time.Second / sampRate } -func (d *Streamer) Stream(samples [][2]float64) (n int, ok bool) { - if d.pos >= d.h.dataSize { +func (s *Streamer) Position() time.Duration { + frameIndex := time.Duration(s.pos / int32(s.h.bytesPerFrame)) + frameTime := time.Second / time.Duration(s.h.sampleRate) + return frameIndex * frameTime +} + +func (s *Streamer) Seek(d time.Duration) { + if d < 0 || s.Duration() < d { + panic("wav: seek duration out of range") + } + frame := int32(d / (time.Second / time.Duration(s.h.sampleRate))) + pos := frame * int32(s.h.bytesPerFrame) + _, err := s.rsc.Seek(int64(pos), io.SeekStart) + if err != nil { + s.err = err + return + } + s.pos = pos +} + +func (s *Streamer) Stream(samples [][2]float64) (n int, ok bool) { + if s.pos >= s.h.dataSize { return 0, false } switch { - case d.h.bitsPerSample == 8 && d.h.numChans == 1: + case s.h.bitsPerSample == 8 && s.h.numChans == 1: width := 1 p := make([]byte, len(samples)*width) - n, err := d.rc.Read(p) + n, err := s.rsc.Read(p) for i, j := 0, 0; i < n-width; i, j = i+width, j+1 { val := float64(p[i])/(1<<8-1)*2 - 1 samples[j][0] = val samples[j][1] = val } if err != nil { - d.err = err + s.err = err } - d.pos += int32(n) + s.pos += int32(n) return n / width, true - case d.h.bitsPerSample == 8 && d.h.numChans >= 2: - width := int(d.h.numChans) + case s.h.bitsPerSample == 8 && s.h.numChans >= 2: + width := int(s.h.numChans) p := make([]byte, len(samples)*width) - n, err := d.rc.Read(p) + n, err := s.rsc.Read(p) for i, j := 0, 0; i < n-width; i, j = i+width, j+1 { samples[j][0] = float64(p[i+0])/(1<<8-1)*2 - 1 samples[j][1] = float64(p[i+1])/(1<<8-1)*2 - 1 } if err != nil { - d.err = err + s.err = err } - d.pos += int32(n) + s.pos += int32(n) return n / width, true - case d.h.bitsPerSample == 16 && d.h.numChans == 1: + case s.h.bitsPerSample == 16 && s.h.numChans == 1: width := 2 p := make([]byte, len(samples)*width) - n, err := d.rc.Read(p) + n, err := s.rsc.Read(p) for i, j := 0, 0; i < n-width; i, j = i+width, j+1 { val := float64(int16(p[i+0])+int16(p[i+1])*(1<<8)) / (1<<15 - 1) samples[j][0] = val samples[j][1] = val } if err != nil { - d.err = err + s.err = err } - d.pos += int32(n) + s.pos += int32(n) return n / width, true - case d.h.bitsPerSample == 16 && d.h.numChans >= 2: - width := int(d.h.numChans) * 2 + case s.h.bitsPerSample == 16 && s.h.numChans >= 2: + width := int(s.h.numChans) * 2 p := make([]byte, len(samples)*width) - n, err := d.rc.Read(p) + n, err := s.rsc.Read(p) for i, j := 0, 0; i <= n-width; i, j = i+width, j+1 { samples[j][0] = float64(int16(p[i+0])+int16(p[i+1])*(1<<8)) / (1<<15 - 1) samples[j][1] = float64(int16(p[i+2])+int16(p[i+3])*(1<<8)) / (1<<15 - 1) } if err != nil { - d.err = err + s.err = err } - d.pos += int32(n) + s.pos += int32(n) return n / width, true } panic("unreachable") } -func (d *Streamer) Close() error { - err := d.rc.Close() +func (s *Streamer) Close() error { + err := s.rsc.Close() if err != nil { return errors.Wrap(err, "wav") }