fix: fix data race on micVolume field

micVolume was accessed from both the UI goroutine (SetMicVolume,
GetMicVolume) and the audio source goroutine (sourceRoutine) without
synchronization, a data race under the Go memory model.

Switch to atomic.Uint32 storing float32 bits to make reads and writes
safe across goroutines.
This commit is contained in:
Brandon McGinty (deepseek)
2026-08-08 19:59:07 -04:00
committed by Brandon McGinty
parent 676c27fe30
commit 1bdd7ac52e
+12 -6
View File
@@ -3,8 +3,10 @@ package gumbleopenal
import ( import (
"encoding/binary" "encoding/binary"
"errors" "errors"
"math"
"os/exec" "os/exec"
"sync" "sync"
"sync/atomic"
"time" "time"
"git.stormux.org/storm/barnard/audio" "git.stormux.org/storm/barnard/audio"
@@ -60,7 +62,7 @@ type Stream struct {
sourceFormat openal.Format sourceFormat openal.Format
sourceChannels int sourceChannels int
sourceFrameSize int sourceFrameSize int
micVolume float32 micVolume atomic.Uint32 // float32 stored as bits
sourceStop chan bool sourceStop chan bool
deviceSink *openal.Device deviceSink *openal.Device
@@ -110,7 +112,6 @@ func New(client *gumble.Client, inputDevice *string, outputDevice *string, test
sourceFormat: inputFormat, sourceFormat: inputFormat,
sourceChannels: sourceChannels, sourceChannels: sourceChannels,
sourceFrameSize: frmsz, sourceFrameSize: frmsz,
micVolume: 1.0,
micAGC: audio.NewAGC(), // Always enable AGC for outgoing mic micAGC: audio.NewAGC(), // Always enable AGC for outgoing mic
} }
if sourceChannels == 2 { if sourceChannels == 2 {
@@ -209,7 +210,11 @@ func (s *Stream) StopSource() error {
} }
func (s *Stream) GetMicVolume() float32 { func (s *Stream) GetMicVolume() float32 {
return s.micVolume bits := s.micVolume.Load()
if bits == 0 {
return 1.0 // default on first access
}
return math.Float32frombits(bits)
} }
func (s *Stream) SetMicVolume(change float32, relative bool) { func (s *Stream) SetMicVolume(change float32, relative bool) {
@@ -225,7 +230,7 @@ func (s *Stream) SetMicVolume(change float32, relative bool) {
if val <= 0 { if val <= 0 {
val = 0 val = 0
} }
s.micVolume = val s.micVolume.Store(math.Float32bits(val))
} }
func (s *Stream) OnAudioStream(e *gumble.AudioStreamEvent) { func (s *Stream) OnAudioStream(e *gumble.AudioStreamEvent) {
@@ -417,8 +422,9 @@ func (s *Stream) sourceRoutine(inputDevice *string) {
hasMicInput = true hasMicInput = true
for i := 0; i < sampleCount; i++ { for i := 0; i < sampleCount; i++ {
sample := int16(binary.LittleEndian.Uint16(buff[i*2:])) sample := int16(binary.LittleEndian.Uint16(buff[i*2:]))
if s.micVolume != 1.0 { vol := s.GetMicVolume()
sample = int16(float32(sample) * s.micVolume) if vol != 1.0 {
sample = int16(float32(sample) * vol)
} }
int16Buffer[i] = sample int16Buffer[i] = sample
} }