package player import ( "math" "sync/atomic" "github.com/gopxl/beep/v2" ) // volumeStreamer applies dB gain and optional mono downmix to an audio stream. // Volume and mono are read via atomic operations, eliminating mutex contention // with the UI thread on the audio hot path. type volumeStreamer struct { s beep.Streamer vol *atomic.Uint64 // dB stored as Float64bits mono *atomic.Bool cachedDB float64 // last dB value used to compute cachedGain; starts NaN to force first compute cachedGain float64 // precomputed linear gain = 10^(dB/20) } func (v *volumeStreamer) Stream(samples [][2]float64) (int, bool) { n, ok := v.s.Stream(samples) if n == 0 { return 0, ok } db := math.Float64frombits(v.vol.Load()) mono := v.mono.Load() // Recompute gain only when volume changes (rare) instead of every Stream() call. if db != v.cachedDB { v.cachedGain = math.Pow(10, db/20) v.cachedDB = db } gain := v.cachedGain for i := range n { samples[i][0] *= gain samples[i][1] *= gain if mono { mid := (samples[i][0] + samples[i][1]) / 2 samples[i][0] = mid samples[i][1] = mid } } return n, ok } func (v *volumeStreamer) Err() error { return v.s.Err() }