diff --git a/gumbleopenal/stream.go b/gumbleopenal/stream.go index 49827d7..9db5e5d 100644 --- a/gumbleopenal/stream.go +++ b/gumbleopenal/stream.go @@ -13,7 +13,14 @@ var ( ErrState = errors.New("gumbleopenal: invalid state") ) +type Config struct { + Deafen bool + Mute bool +} + type Stream struct { + Config Config + client *gumble.Client link gumble.Detacher @@ -25,25 +32,41 @@ type Stream struct { contextSink *openal.Context } -func New(client *gumble.Client) (*Stream, error) { +func Setup(client *gumble.Client, conf Config) (*Stream, error) { s := &Stream{ + Config: conf, client: client, sourceFrameSize: client.Config.AudioFrameSize(), } - s.deviceSource = openal.CaptureOpenDevice("", gumble.AudioSampleRate, openal.FormatMono16, uint32(s.sourceFrameSize)) + if !conf.Mute { + s.deviceSource = openal.CaptureOpenDevice("", gumble.AudioSampleRate, openal.FormatMono16, uint32(s.sourceFrameSize)) + s.StartSource() + } - s.deviceSink = openal.OpenDevice("") - s.contextSink = s.deviceSink.CreateContext() - s.contextSink.Activate() + if !conf.Deafen { + s.deviceSink = openal.OpenDevice("") + s.contextSink = s.deviceSink.CreateContext() + s.contextSink.Activate() - s.link = client.Config.AttachAudio(s) + s.link = client.Config.AttachAudio(s) + } return s, nil } +func New(client *gumble.Client) (*Stream, error) { + s, err := Setup(client, Config{Mute: true, Deafen: false}) + if err == nil { + s.deviceSource = openal.CaptureOpenDevice("", gumble.AudioSampleRate, openal.FormatMono16, uint32(s.sourceFrameSize)) + } + return s, err +} + func (s *Stream) Destroy() { - s.link.Detach() + if s.link != nil { + s.link.Detach() + } if s.deviceSource != nil { s.StopSource() s.deviceSource.CaptureCloseDevice() @@ -80,6 +103,14 @@ func (s *Stream) StopSource() error { func (s *Stream) OnAudioStream(e *gumble.AudioStreamEvent) { go func() { source := openal.NewSource() + emptyBufs := openal.NewBuffers(8) + reclaim := func() { + if n := source.BuffersProcessed(); n > 0 { + reclaimedBufs := make(openal.Buffers, n) + source.UnqueueBuffers(reclaimedBufs) + emptyBufs = append(emptyBufs, reclaimedBufs...) + } + } var raw [gumble.AudioMaximumFrameSize * 2]byte for packet := range e.C { samples := len(packet.AudioBuffer) @@ -89,19 +120,21 @@ func (s *Stream) OnAudioStream(e *gumble.AudioStreamEvent) { for i, value := range packet.AudioBuffer { binary.LittleEndian.PutUint16(raw[i*2:], uint16(value)) } - for source.BuffersProcessed() > 0 { - source.UnqueueBuffer().Delete() + reclaim() + if len(emptyBufs) == 0 { + continue } - buffer := openal.NewBuffer() + last := len(emptyBufs) - 1 + buffer := emptyBufs[last] + emptyBufs = emptyBufs[:last] buffer.SetData(openal.FormatMono16, raw[:samples*2], gumble.AudioSampleRate) source.QueueBuffer(buffer) if source.State() != openal.Playing { source.Play() } } - for source.BuffersProcessed() > 0 { - source.UnqueueBuffer().Delete() - } + reclaim() + emptyBufs.Delete() source.Delete() }() }