diff --git a/fileplayback/player.go b/fileplayback/player.go index 5f1daf7..90e492d 100644 --- a/fileplayback/player.go +++ b/fileplayback/player.go @@ -10,22 +10,19 @@ import ( "time" "git.stormux.org/storm/barnard/gumble/gumble" - "git.stormux.org/storm/barnard/gumble/go-openal/openal" ) // Player handles file playback and mixing with microphone audio type Player struct { - client *gumble.Client - filename string - audioChan chan gumble.AudioBuffer - stopChan chan struct{} - mutex sync.Mutex - playing bool - errorFunc func(error) + client *gumble.Client + filename string + audioChan chan gumble.AudioBuffer + stopChan chan struct{} + mutex sync.Mutex + playing bool + errorFunc func(error) - // Local playback - localSource *openal.Source - localBuffers openal.Buffers + localPlayback func([]byte) } // New creates a new file player @@ -44,6 +41,14 @@ func (p *Player) SetErrorFunc(f func(error)) { p.errorFunc = f } +// SetLocalPlayback sets the callback that plays file audio locally. The +// callback is called with nil when playback stops and should release resources. +func (p *Player) SetLocalPlayback(f func([]byte)) { + p.mutex.Lock() + defer p.mutex.Unlock() + p.localPlayback = f +} + func (p *Player) reportError(err error) { p.mutex.Lock() errorFunc := p.errorFunc @@ -65,14 +70,6 @@ func (p *Player) PlayFile(filename string) error { p.filename = filename - // Initialize local playback - source := openal.NewSource() - p.localSource = &source - p.localSource.SetGain(1.0) - - // Create buffers for local playback - p.localBuffers = openal.NewBuffers(64) - // Start the file reading goroutine p.playing = true p.stopChan = make(chan struct{}) @@ -93,15 +90,8 @@ func (p *Player) Stop() error { close(p.stopChan) p.playing = false - // Clean up local playback - if p.localSource != nil { - p.localSource.Stop() - p.localSource.Delete() - p.localSource = nil - } - if p.localBuffers != nil { - p.localBuffers.Delete() - p.localBuffers = nil + if p.localPlayback != nil { + p.localPlayback(nil) } // Drain the audio channel @@ -129,32 +119,12 @@ func (p *Player) GetAudioFrame() []int16 { } } -// playLocalAudio plays audio through the local OpenAL source func (p *Player) playLocalAudio(data []byte) { - if p.localSource == nil { - return - } - - // Reclaim processed buffers - if n := p.localSource.BuffersProcessed(); n > 0 { - reclaimedBufs := make(openal.Buffers, n) - p.localSource.UnqueueBuffers(reclaimedBufs) - p.localBuffers = append(p.localBuffers, reclaimedBufs...) - } - - // If we have available buffers, queue more audio - if len(p.localBuffers) > 0 { - buffer := p.localBuffers[len(p.localBuffers)-1] - p.localBuffers = p.localBuffers[:len(p.localBuffers)-1] - - // Set buffer data as stereo - buffer.SetData(openal.FormatStereo16, data, gumble.AudioSampleRate) - p.localSource.QueueBuffer(buffer) - - // Start playing if not already - if p.localSource.State() != openal.Playing { - p.localSource.Play() - } + p.mutex.Lock() + localPlayback := p.localPlayback + p.mutex.Unlock() + if localPlayback != nil { + localPlayback(data) } } @@ -204,15 +174,8 @@ func (p *Player) readFileAudio() { // File finished playing p.mutex.Lock() p.playing = false - // Clean up local playback - if p.localSource != nil { - p.localSource.Stop() - p.localSource.Delete() - p.localSource = nil - } - if p.localBuffers != nil { - p.localBuffers.Delete() - p.localBuffers = nil + if p.localPlayback != nil { + p.localPlayback(nil) } p.mutex.Unlock() cmd.Wait() diff --git a/gumble/gumbleopenal/stream.go b/gumble/gumbleopenal/stream.go index b5a7872..80cf30c 100644 --- a/gumble/gumbleopenal/stream.go +++ b/gumble/gumbleopenal/stream.go @@ -82,6 +82,8 @@ type Stream struct { micAGC *audio.AGC micAGCRight *audio.AGC filePlayer FilePlayer + localSource *openal.Source + localBuffers openal.Buffers recorderMu sync.RWMutex errorFunc func(error) // called on capture errors recorder Recorder @@ -200,6 +202,54 @@ func (s *Stream) SetNoiseProcessor(np NoiseProcessor) { func (s *Stream) SetFilePlayer(fp FilePlayer) { s.filePlayer = fp + if player, ok := fp.(interface{ SetLocalPlayback(func([]byte)) }); ok { + player.SetLocalPlayback(s.playLocalAudio) + } +} + +func (s *Stream) playLocalAudio(data []byte) { + s.render(func() { + if data == nil { + if s.localSource != nil { + s.localSource.Stop() + queued := s.localSource.BuffersQueued() + if queued > 0 { + buffers := make(openal.Buffers, queued) + s.localSource.UnqueueBuffers(buffers) + s.localBuffers = append(s.localBuffers, buffers...) + } + s.localSource.Delete() + s.localSource = nil + } + if len(s.localBuffers) > 0 { + s.localBuffers.Delete() + s.localBuffers = nil + } + return + } + if s.localSource == nil { + source := openal.NewSource() + source.SetGain(1) + s.localSource = &source + s.localBuffers = openal.NewBuffers(64) + } + if n := s.localSource.BuffersProcessed(); n > 0 { + buffers := make(openal.Buffers, n) + s.localSource.UnqueueBuffers(buffers) + s.localBuffers = append(s.localBuffers, buffers...) + } + if len(s.localBuffers) == 0 { + return + } + last := len(s.localBuffers) - 1 + buffer := s.localBuffers[last] + s.localBuffers = s.localBuffers[:last] + buffer.SetData(openal.FormatStereo16, data, gumble.AudioSampleRate) + s.localSource.QueueBuffer(buffer) + if s.localSource.State() != openal.Playing { + s.localSource.Play() + } + }) } func (s *Stream) GetFilePlayer() FilePlayer {