119 lines
2.2 KiB
Go
119 lines
2.2 KiB
Go
package audio
|
|
|
|
import (
|
|
"errors"
|
|
|
|
"io"
|
|
|
|
"github.com/hajimehoshi/oto"
|
|
)
|
|
|
|
type Speaker interface {
|
|
Play(Streamer)
|
|
Update() error
|
|
}
|
|
|
|
type DefaultSpeaker struct {
|
|
Streamer
|
|
isPlaying bool
|
|
samples [][2]float64
|
|
player *oto.Player
|
|
buf []uint8
|
|
bufferSize int
|
|
}
|
|
|
|
var (
|
|
ErrBufferMustBePowerOf2 = errors.New("Buffer passed to Read must be a power of 2")
|
|
)
|
|
|
|
func (s *DefaultSpeaker) Read(dst []byte) (n int, err error) {
|
|
if !s.isPlaying || s.eof() {
|
|
return 0, io.EOF
|
|
}
|
|
// we need dst to be a power of two in order for us to write samples cleanly
|
|
if len(dst)%2 != 0 {
|
|
return 0, ErrBufferMustBePowerOf2
|
|
}
|
|
|
|
if l := len(dst); l > 1 {
|
|
for n < l-1 {
|
|
sample := s.readSample()
|
|
dst[n] = byte(sample[0])
|
|
dst[n+1] = byte(sample[1])
|
|
if s.eof() {
|
|
s.samples = make([][2]float64, s.bufferSize/2)
|
|
break
|
|
}
|
|
n += 2
|
|
}
|
|
}
|
|
return n, nil
|
|
}
|
|
|
|
func (s *DefaultSpeaker) eof() bool {
|
|
return len(s.samples) == 0
|
|
}
|
|
|
|
type Sample [2]float64
|
|
|
|
func (s *DefaultSpeaker) readSample() Sample {
|
|
sample := s.samples[0]
|
|
s.samples = s.samples[1:]
|
|
return sample
|
|
}
|
|
|
|
func NewDefaultSpeaker(bufferSize int) (*DefaultSpeaker, error) {
|
|
p, err := oto.NewPlayer(int(SampleRate), 2, 2, bufferSize)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &DefaultSpeaker{
|
|
player: p,
|
|
samples: make([][2]float64, bufferSize/2),
|
|
buf: make([]uint8, bufferSize),
|
|
bufferSize: bufferSize,
|
|
}, nil
|
|
}
|
|
|
|
func (bs *DefaultSpeaker) Play(s Streamer) {
|
|
bs.isPlaying = true
|
|
bs.Streamer = s
|
|
}
|
|
|
|
func (b *DefaultSpeaker) Update() error {
|
|
|
|
if b.isPlaying {
|
|
n, ok := b.Stream(b.samples)
|
|
if n == len(b.samples) && ok {
|
|
r, err := b.Read(b.buf)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
b.buf = b.buf[:r]
|
|
_, err = b.player.Write(b.buf)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
b.buf = make([]byte, b.bufferSize)
|
|
}
|
|
// we're read bytes but drained the streamer, so copy data and stop playing
|
|
if n > 0 && n < len(b.samples) && ok {
|
|
r, err := b.Read(b.buf)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
b.buf = b.buf[:r]
|
|
_, err = b.player.Write(b.buf)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
b.isPlaying = false
|
|
}
|
|
// this stream is already drained, set isPlaying to false
|
|
if n == 0 && !ok {
|
|
b.isPlaying = false
|
|
}
|
|
}
|
|
return nil
|
|
}
|