diff --git a/opus/decode.go b/opus/decode.go new file mode 100644 index 0000000..d2a82e2 --- /dev/null +++ b/opus/decode.go @@ -0,0 +1,87 @@ +package opus + +import ( + "io" + "log" + "sync" + + "github.com/faiface/beep" + "github.com/pkg/errors" + opusd "gopkg.in/hraban/opus.v2" +) + +type decoder struct { + sync.Mutex + + rc io.ReadSeekCloser + r *opusd.Stream + err error +} + +func New(f io.ReadSeekCloser) (beep.StreamSeeker, error) { + d := new(decoder) + st, err := opusd.NewStream(f) + if err != nil { + return d, err + } + + d.rc = f + d.r = st + + return d, nil +} + +func (s *decoder) Err() error { return s.err } + +func (d *decoder) Stream(samples [][2]float64) (n int, ok bool) { + if d.err != nil { + return 0, false + } + var tmp [2]float32 + for i := range samples { + dn, err := d.r.ReadFloat32(tmp[:]) + + if dn == 1 { + samples[i][0], samples[i][1] = float64(tmp[0]), float64(tmp[1]) + n++ + ok = true + } + if err == io.EOF { + ok = false + break + } + if err != nil { + ok = false + d.err = errors.Wrap(err, "ogg/opus") + break + } + } + + return n, ok +} + +func (d *decoder) Len() int { + return 0 +} + +func (d *decoder) Position() int { + return 0 +} + +func (d *decoder) Seek(p int) error { + _, err := d.rc.Seek(int64(p), io.SeekStart) + if err != nil { + log.Println(err) + return errors.Wrap(err, "ogg/opus") + } + + st, err := opusd.NewStream(d.rc) + if err != nil { + log.Println(err) + return errors.Wrap(err, "ogg/opus") + } + + d.r = st + + return nil +} diff --git a/opus/decode_test.go b/opus/decode_test.go new file mode 100644 index 0000000..14d7079 --- /dev/null +++ b/opus/decode_test.go @@ -0,0 +1,79 @@ +package opus + +import ( + "context" + "log" + "os" + "sync" + "testing" + "time" + + "github.com/faiface/beep" +) + +var ( + mu sync.Mutex + mixer beep.Mixer + frameSize int = 960 + samples = make([][2]float64, frameSize) + buf []byte + maxBytes int = (frameSize * 2) * 2 + done chan struct{} +) + +func update() { + mu.Lock() + mixer.Stream(samples) + mu.Unlock() + + for i := range samples { + for c := range samples[i] { + val := samples[i][c] + if val < -1 { + val = -1 + } + if val > +1 { + val = +1 + } + valInt16 := int16(val * (1<<15 - 1)) + low := byte(valInt16) + high := byte(valInt16 >> 8) + buf[i*4+c*2+0] = low + buf[i*4+c*2+1] = high + } + } + + //log.Printf("%+v", buf) +} + +func TestMain(t *testing.T) { + buf = make([]byte, maxBytes) + + ctx, cancel := context.WithTimeout(context.Background(), time.Minute*9) + defer cancel() + + file, err := os.Open("test.opus") + if err != nil { + log.Fatal(err) + } + + d, err := New(file) + if err != nil { + log.Fatal(err) + } + + loop := beep.Loop(-1, d) + + mu.Lock() + mixer.Add(loop) + mu.Unlock() + + for { + select { + default: + update() + case <-ctx.Done(): + return + } + } +}