Files
bjarneo--cliamp/ui/visualizer_bench_test.go
wehub-resource-sync ead81af521
CI / go (push) Waiting to run
CI / build (darwin, macos-14) (push) Waiting to run
CI / build (windows, windows-2025) (push) Waiting to run
Deploy to GitHub Pages / deploy (push) Failing after 0s
chore: import upstream snapshot with attribution
2026-07-13 12:31:13 +08:00

130 lines
3.0 KiB
Go

package ui
import (
"math"
"math/rand/v2"
"testing"
"time"
)
// benchSampleRate matches the default mixer sample rate used elsewhere in the package.
const benchSampleRate = 44100
// benchSamplesSine fills a buffer with a 440 Hz sine wave — a clean, single-tone
// workload that exercises the FFT without triggering any silence short-circuit.
func benchSamplesSine(n int) []float64 {
buf := make([]float64, n)
for i := range buf {
buf[i] = math.Sin(2 * math.Pi * 440 * float64(i) / float64(benchSampleRate))
}
return buf
}
// benchSamplesNoise fills a buffer with deterministic white noise — the worst
// case for per-band averaging (energy spread across every bin).
func benchSamplesNoise(n int) []float64 {
r := rand.New(rand.NewPCG(1, 2))
buf := make([]float64, n)
for i := range buf {
buf[i] = r.Float64()*2 - 1
}
return buf
}
// benchSamplesSilence returns a zeroed buffer — the common case between tracks
// or during pause, where ideally we'd skip most of the FFT pipeline.
func benchSamplesSilence(n int) []float64 {
return make([]float64, n)
}
func BenchmarkAnalyze(b *testing.B) {
spec := spectrumAnalysisSpec(DefaultSpectrumBands)
cases := []struct {
name string
samples []float64
}{
{"Sine440", benchSamplesSine(defaultFFTSize)},
{"Noise", benchSamplesNoise(defaultFFTSize)},
{"Silence", benchSamplesSilence(defaultFFTSize)},
}
for _, tc := range cases {
b.Run(tc.name, func(b *testing.B) {
v := NewVisualizer(benchSampleRate)
b.ReportAllocs()
b.ResetTimer()
for b.Loop() {
v.Analyze(tc.samples, spec)
}
})
}
}
// benchDriverModes covers the representative rendering shapes: pure spectrum
// bars, peak-cap animation, frame-driven animation, waveform oscilloscope.
var benchDriverModes = []VisMode{
VisBars,
VisBarsDot,
VisBarsOutline,
VisClassicPeak,
VisMatrix,
VisRain,
VisFlame,
VisLogo,
VisPulse,
VisWave,
VisScope,
}
func BenchmarkRender(b *testing.B) {
samples := benchSamplesSine(defaultFFTSize)
for _, mode := range benchDriverModes {
name := visModes[mode].name
b.Run(name, func(b *testing.B) {
v := NewVisualizer(benchSampleRate)
v.Mode = mode
v.Rows = 5
// Prime driver state and bands so Render reflects realistic output.
ctx := VisTickContext{
Now: time.Now(),
Playing: true,
Analyze: func(spec VisAnalysisSpec) []float64 {
return v.Analyze(samples, spec)
},
}
for range 4 {
v.Tick(ctx)
}
b.ReportAllocs()
b.ResetTimer()
for b.Loop() {
_ = v.Render()
}
})
}
}
func BenchmarkTickPipeline(b *testing.B) {
samples := benchSamplesSine(defaultFFTSize)
for _, mode := range benchDriverModes {
name := visModes[mode].name
b.Run(name, func(b *testing.B) {
v := NewVisualizer(benchSampleRate)
v.Mode = mode
v.Rows = 5
ctx := VisTickContext{
Now: time.Now(),
Playing: true,
Analyze: func(spec VisAnalysisSpec) []float64 {
return v.Analyze(samples, spec)
},
}
b.ReportAllocs()
b.ResetTimer()
for b.Loop() {
v.Tick(ctx)
_ = v.Render()
}
})
}
}