Skip to content

Commit a761b08

Browse files
committed
Increase tick rate for waveform visualizers from 20 to 30 FPS
Waveform modes (wave, scope, heartbeat) skip FFT entirely, so they can tick faster without meaningful CPU cost. Adds TickWave (32ms) alongside TickFast (50ms) and reuses the y-position buffer across frames to reduce GC pressure.
1 parent 2b5d3aa commit a761b08

3 files changed

Lines changed: 25 additions & 8 deletions

File tree

ui/tick.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import "time"
44

55
// Tick intervals: fast for visualizer animation, slow for time/seek display.
66
const (
7-
TickFast = 50 * time.Millisecond // 20 FPS — visualizer active
7+
TickWave = 32 * time.Millisecond // ~30 FPS — waveform modes (no FFT)
8+
TickFast = 50 * time.Millisecond // 20 FPS — spectrum modes (FFT)
89
TickSlow = 200 * time.Millisecond // 5 FPS — visualizer off or overlay
910
)

ui/vis_wave.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,12 @@ func (v *Visualizer) renderWave() string {
1414
n := len(samples)
1515

1616
// Downsample audio to one y-position per horizontal dot column.
17-
ypos := make([]int, dotCols)
17+
if cap(v.waveYBuf) >= dotCols {
18+
v.waveYBuf = v.waveYBuf[:dotCols]
19+
} else {
20+
v.waveYBuf = make([]int, dotCols)
21+
}
22+
ypos := v.waveYBuf
1823
for x := range dotCols {
1924
var sample float64
2025
if n > 0 {

ui/visualizer.go

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -231,8 +231,9 @@ type visEntry struct {
231231
}
232232

233233
type renderOnlyDriver struct {
234-
spec VisAnalysisSpec
235-
render func(*Visualizer, []float64) string
234+
spec VisAnalysisSpec
235+
render func(*Visualizer, []float64) string
236+
tickDuration time.Duration // 0 = use defaultDriverTickInterval
236237
}
237238

238239
func (d *renderOnlyDriver) AnalysisSpec(*Visualizer) VisAnalysisSpec {
@@ -247,7 +248,10 @@ func (d *renderOnlyDriver) Tick(v *Visualizer, ctx VisTickContext) {
247248
defaultDriverTick(v, ctx, d.spec)
248249
}
249250

250-
func (*renderOnlyDriver) TickInterval(_ *Visualizer, ctx VisTickContext) time.Duration {
251+
func (d *renderOnlyDriver) TickInterval(_ *Visualizer, ctx VisTickContext) time.Duration {
252+
if d.tickDuration > 0 && ctx.Playing && !ctx.OverlayActive {
253+
return d.tickDuration
254+
}
251255
return defaultDriverTickInterval(ctx)
252256
}
253257

@@ -275,6 +279,12 @@ func newRenderOnlyDriver(spec VisAnalysisSpec, render func(*Visualizer, []float6
275279
}
276280
}
277281

282+
func newFastRenderOnlyDriver(spec VisAnalysisSpec, tick time.Duration, render func(*Visualizer, []float64) string) func() visModeDriver {
283+
return func() visModeDriver {
284+
return &renderOnlyDriver{spec: NormalizeAnalysisSpec(spec), render: render, tickDuration: tick}
285+
}
286+
}
287+
278288
func newNoOpDriver() visModeDriver {
279289
return &noOpDriver{}
280290
}
@@ -315,6 +325,7 @@ type Visualizer struct {
315325
Mode VisMode
316326
Rows int // display height in terminal rows (default 5)
317327
waveBuf []float64 // raw samples for wave mode
328+
waveYBuf []int // reusable y-position buffer for wave rendering
318329
frame uint64 // tick-driven animation clock
319330
sampleBuf []float64 // reusable buffer for reading audio tap samples
320331
drivers [VisCount]visModeDriver
@@ -362,7 +373,7 @@ var visModes = [VisCount]visEntry{
362373
VisBricks: {"Bricks", newRenderOnlyDriver(spectrumAnalysisSpec(DefaultSpectrumBands), (*Visualizer).renderBricks)},
363374
VisColumns: {"Columns", newRenderOnlyDriver(spectrumAnalysisSpec(DefaultSpectrumBands), (*Visualizer).renderColumns)},
364375
VisClassicPeak: {"ClassicPeak", newClassicPeakDriver},
365-
VisWave: {"Wave", newRenderOnlyDriver(spectrumAnalysisSpec(0), func(v *Visualizer, _ []float64) string { return v.renderWave() })},
376+
VisWave: {"Wave", newFastRenderOnlyDriver(spectrumAnalysisSpec(0), TickWave, func(v *Visualizer, _ []float64) string { return v.renderWave() })},
366377
VisScatter: {"Scatter", newRenderOnlyDriver(spectrumAnalysisSpec(DefaultSpectrumBands), (*Visualizer).renderScatter)},
367378
VisFlame: {"Flame", newRenderOnlyDriver(spectrumAnalysisSpec(DefaultSpectrumBands), (*Visualizer).renderFlame)},
368379
VisRetro: {"Retro", newRenderOnlyDriver(spectrumAnalysisSpec(DefaultSpectrumBands), (*Visualizer).renderRetro)},
@@ -373,8 +384,8 @@ var visModes = [VisCount]visEntry{
373384
VisFirework: {"Firework", newRenderOnlyDriver(spectrumAnalysisSpec(DefaultSpectrumBands), (*Visualizer).renderFirework)},
374385
VisLogo: {"Logo", newRenderOnlyDriver(spectrumAnalysisSpec(DefaultSpectrumBands), (*Visualizer).renderLogo)},
375386
VisTerrain: {"Terrain", newTerrainDriver},
376-
VisScope: {"Scope", newRenderOnlyDriver(spectrumAnalysisSpec(0), func(v *Visualizer, _ []float64) string { return v.renderScope() })},
377-
VisHeartbeat: {"Heartbeat", newRenderOnlyDriver(spectrumAnalysisSpec(0), func(v *Visualizer, _ []float64) string { return v.renderHeartbeat() })},
387+
VisScope: {"Scope", newFastRenderOnlyDriver(spectrumAnalysisSpec(0), TickWave, func(v *Visualizer, _ []float64) string { return v.renderScope() })},
388+
VisHeartbeat: {"Heartbeat", newFastRenderOnlyDriver(spectrumAnalysisSpec(0), TickWave, func(v *Visualizer, _ []float64) string { return v.renderHeartbeat() })},
378389
VisButterfly: {"Butterfly", newRenderOnlyDriver(spectrumAnalysisSpec(DefaultSpectrumBands), (*Visualizer).renderButterfly)},
379390
VisAscii: {"Ascii", newRenderOnlyDriver(spectrumAnalysisSpec(DefaultSpectrumBands), (*Visualizer).renderAscii)},
380391
VisNone: {"None", newNoOpDriver},

0 commit comments

Comments
 (0)