Skip to content

Commit ddd6cb5

Browse files
committed
Reduce GPU usage when visualizer is off by adaptive tick rate (50ms→200ms), skip FFT on VisNone, and replace per-frame lipgloss allocations with string padding
1 parent b6987e4 commit ddd6cb5

2 files changed

Lines changed: 49 additions & 11 deletions

File tree

ui/model.go

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,12 @@ const (
5757
type tickMsg time.Time
5858
type autoPlayMsg struct{}
5959

60+
// Tick intervals: fast for visualizer animation, slow for time/seek display.
61+
const (
62+
tickFast = 50 * time.Millisecond // 20 FPS — visualizer active
63+
tickSlow = 200 * time.Millisecond // 5 FPS — visualizer off or overlay
64+
)
65+
6066
// Model is the Bubbletea model for the CLIAMP TUI.
6167
type Model struct {
6268
player *player.Player
@@ -225,6 +231,15 @@ func (m Model) ThemeName() string {
225231
return m.themes[m.themeIdx].Name
226232
}
227233

234+
// isOverlayActive reports whether a full-screen overlay is shown instead of
235+
// the main player view. When true, the visualizer is not visible and we can
236+
// use the slower tick rate.
237+
func (m *Model) isOverlayActive() bool {
238+
return m.showKeymap || m.showThemes || m.showFileBrowser ||
239+
m.showNavBrowser || m.showPlManager || m.showQueue ||
240+
m.showInfo || m.searching
241+
}
242+
228243
// openThemePicker re-loads themes from disk (picking up new user files)
229244
// and opens the theme selector overlay.
230245
func (m *Model) openThemePicker() {
@@ -596,7 +611,11 @@ func (m Model) Init() tea.Cmd {
596611
}
597612

598613
func tickCmd() tea.Cmd {
599-
return tea.Tick(time.Millisecond*50, func(t time.Time) tea.Msg {
614+
return tickCmdAt(tickFast)
615+
}
616+
617+
func tickCmdAt(d time.Duration) tea.Cmd {
618+
return tea.Tick(d, func(t time.Time) tea.Msg {
600619
return tickMsg(t)
601620
})
602621
}
@@ -662,7 +681,15 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
662681
if m.player.IsPlaying() && !m.player.IsPaused() {
663682
m.titleOff++
664683
}
665-
cmds = append(cmds, tickCmd())
684+
685+
// Use fast ticks only when the visualizer is rendering; otherwise
686+
// slow ticks are enough for time/seek updates and save significant
687+
// GPU repaints in terminal emulators.
688+
interval := tickSlow
689+
if m.vis.Mode != VisNone && !m.isOverlayActive() {
690+
interval = tickFast
691+
}
692+
cmds = append(cmds, tickCmdAt(interval))
666693
return m, tea.Batch(cmds...)
667694

668695
case []playlist.PlaylistInfo:

ui/view.go

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -91,24 +91,32 @@ func (m Model) View() string {
9191
content := strings.Join(sections, "\n")
9292
frame := frameStyle.Render(content)
9393

94-
// Center horizontally and vertically within the terminal
94+
return m.centerFrame(frame)
95+
}
96+
97+
// centerFrame centers a pre-rendered frame in the terminal using plain string
98+
// padding instead of allocating a new lipgloss.Style every render.
99+
func (m Model) centerFrame(frame string) string {
95100
frameW := lipgloss.Width(frame)
96101
frameH := lipgloss.Height(frame)
97-
98102
padLeft := max(0, (m.width-frameW)/2)
99103
padTop := max(0, (m.height-frameH)/2)
100104

101-
return strings.Repeat("\n", padTop) +
102-
lipgloss.NewStyle().MarginLeft(padLeft).Render(frame)
105+
if padLeft == 0 {
106+
return strings.Repeat("\n", padTop) + frame
107+
}
108+
// Indent every line by padLeft spaces.
109+
prefix := strings.Repeat(" ", padLeft)
110+
lines := strings.Split(frame, "\n")
111+
for i, l := range lines {
112+
lines[i] = prefix + l
113+
}
114+
return strings.Repeat("\n", padTop) + strings.Join(lines, "\n")
103115
}
104116

105117
// centerOverlay wraps content in a frame and centers it in the terminal.
106118
func (m Model) centerOverlay(content string) string {
107-
frame := frameStyle.Render(content)
108-
padLeft := max(0, (m.width-lipgloss.Width(frame))/2)
109-
padTop := max(0, (m.height-lipgloss.Height(frame))/2)
110-
return strings.Repeat("\n", padTop) +
111-
lipgloss.NewStyle().MarginLeft(padLeft).Render(frame)
119+
return m.centerFrame(frameStyle.Render(content))
112120
}
113121

114122
func (m Model) renderKeymapOverlay() string {
@@ -487,6 +495,9 @@ func (m Model) renderTimeStatus() string {
487495
}
488496

489497
func (m Model) renderSpectrum() string {
498+
if m.vis.Mode == VisNone {
499+
return ""
500+
}
490501
bands := m.vis.Analyze(m.player.Samples())
491502
return m.vis.Render(bands)
492503
}

0 commit comments

Comments
 (0)