47 lines
1.2 KiB
Go
47 lines
1.2 KiB
Go
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() }
|