Add context to remaining OpenAL device errors

This commit is contained in:
Brandon McGinty (chatgpt) authored and Brandon McGinty committed 2026-08-09 22:09:02 -04:00
1 parent 0712e3150c
commit 21e0627509
2 files changed
+34 -21

No files matched your search

+25 -21
View File
@@ -70,14 +70,16 @@ type Stream struct {
client *gumble.Client client *gumble.Client
link gumble.Detacher link gumble.Detacher
deviceSource *openal.CaptureDevice deviceSource *openal.CaptureDevice
sourceFormat openal.Format inputDeviceName string
sourceChannels int outputDeviceName string
sourceFrameSize int sourceFormat openal.Format
micVolume atomic.Uint32 // float32 stored as bits sourceChannels int
sourceMu sync.Mutex sourceFrameSize int
sourceStop chan bool micVolume atomic.Uint32 // float32 stored as bits
sourceDone chan struct{} sourceMu sync.Mutex
sourceStop chan bool
sourceDone chan struct{}
deviceSink *openal.Device deviceSink *openal.Device
contextSink *openal.Context contextSink *openal.Context
@@ -154,11 +156,13 @@ func New(client *gumble.Client, inputDevice *string, outputDevice *string, test
} }
s := &Stream{ s := &Stream{
client: client, client: client,
sourceFormat: inputFormat, inputDeviceName: devName,
sourceChannels: sourceChannels, outputDeviceName: outName,
sourceFrameSize: frmsz, sourceFormat: inputFormat,
micAGC: audio.NewAGC(), // Always enable AGC for outgoing mic sourceChannels: sourceChannels,
sourceFrameSize: frmsz,
micAGC: audio.NewAGC(), // Always enable AGC for outgoing mic
} }
s.micVolume.Store(math.Float32bits(1.0)) s.micVolume.Store(math.Float32bits(1.0))
if sourceChannels == 2 { if sourceChannels == 2 {
@@ -167,21 +171,21 @@ func New(client *gumble.Client, inputDevice *string, outputDevice *string, test
s.deviceSource = idev s.deviceSource = idev
if s.deviceSource == nil { if s.deviceSource == nil {
return nil, ErrInputDevice return nil, fmt.Errorf("%w: capture device %q is unavailable", ErrInputDevice, deviceName(devName))
} }
s.deviceSink = odev s.deviceSink = odev
if s.deviceSink == nil { if s.deviceSink == nil {
return nil, ErrOutputDevice return nil, fmt.Errorf("%w: playback device %q is unavailable", ErrOutputDevice, deviceName(outName))
} }
s.contextSink = s.deviceSink.CreateContext() s.contextSink = s.deviceSink.CreateContext()
if s.contextSink == nil { if s.contextSink == nil {
err := s.deviceSink.Err() err := s.deviceSink.Err()
s.Destroy() s.Destroy()
if err != nil { if err != nil {
return nil, fmt.Errorf("%w: %v", ErrOutputDevice, err) return nil, fmt.Errorf("%w: creating context for playback device %q: %v", ErrOutputDevice, deviceName(outName), err)
} }
return nil, ErrOutputDevice return nil, fmt.Errorf("%w: could not create context for playback device %q", ErrOutputDevice, deviceName(outName))
} }
// OpenAL contexts are current to an OS thread. Move ownership to one // OpenAL contexts are current to an OS thread. Move ownership to one
// dedicated render thread before any source or buffer is created. // dedicated render thread before any source or buffer is created.
@@ -360,11 +364,11 @@ func (s *Stream) StartSource(inputDevice *string) error {
return ErrState return ErrState
} }
if s.deviceSource == nil { if s.deviceSource == nil {
return ErrMic return fmt.Errorf("%w: capture device %q is unavailable", ErrMic, deviceName(s.inputDeviceName))
} }
s.deviceSource.CaptureStart() s.deviceSource.CaptureStart()
if err := s.deviceSource.Err(); err != nil { if err := s.deviceSource.Err(); err != nil {
return fmt.Errorf("%w: %v", ErrMic, err) return fmt.Errorf("%w: starting capture device %q: %v", ErrMic, deviceName(s.inputDeviceName), err)
} }
stop := make(chan bool) stop := make(chan bool)
done := make(chan struct{}) done := make(chan struct{})
@@ -386,11 +390,11 @@ func (s *Stream) StopSource() error {
// The routine owns capture access; wait for it before closing/reusing it. // The routine owns capture access; wait for it before closing/reusing it.
<-done <-done
if s.deviceSource == nil { if s.deviceSource == nil {
return ErrMic return fmt.Errorf("%w: capture device %q is unavailable", ErrMic, deviceName(s.inputDeviceName))
} }
s.deviceSource.CaptureStop() s.deviceSource.CaptureStop()
if err := s.deviceSource.Err(); err != nil { if err := s.deviceSource.Err(); err != nil {
return fmt.Errorf("%w: %v", ErrMic, err) return fmt.Errorf("%w: stopping capture device %q: %v", ErrMic, deviceName(s.inputDeviceName), err)
} }
return nil return nil
} }
@@ -25,6 +25,15 @@ func TestDeviceOpenErrorsIncludeConfiguredDevice(t *testing.T) {
} }
} }
// Regression: later capture start failures also omitted the configured device.
func TestStartSourceUnavailableDeviceIncludesName(t *testing.T) {
s := &Stream{inputDeviceName: "virtual_mic.monitor"}
err := s.StartSource(nil)
if !errors.Is(err, ErrMic) || !strings.Contains(err.Error(), "virtual_mic.monitor") {
t.Fatalf("start error %q", err)
}
}
func TestStopSourceWaitsForWorker(t *testing.T) { func TestStopSourceWaitsForWorker(t *testing.T) {
stop, done := make(chan bool), make(chan struct{}) stop, done := make(chan bool), make(chan struct{})
s := &Stream{sourceStop: stop, sourceDone: done} s := &Stream{sourceStop: stop, sourceDone: done}