feat(tui): add interactive 5-band per-user equalizer
This commit is contained in:
@@ -120,6 +120,9 @@ type Model struct {
|
||||
showUserView bool
|
||||
viewUser *UserNode
|
||||
pokeID uint16 // Target ID for pending poke
|
||||
|
||||
// Interactive EQ
|
||||
eqBandIdx int // 0-4
|
||||
}
|
||||
|
||||
// addLog adds a message to the log panel
|
||||
@@ -820,6 +823,28 @@ func (m *Model) handleUserViewKeys(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
|
||||
newVol = 0.0
|
||||
}
|
||||
m.audioPlayer.SetUserVolume(u.ID, newVol)
|
||||
|
||||
case "right", "l":
|
||||
// Increase Gain for selected band
|
||||
current := m.audioPlayer.GetUserGain(u.ID, m.eqBandIdx)
|
||||
m.audioPlayer.SetUserGain(u.ID, m.eqBandIdx, current+1.0)
|
||||
|
||||
case "left", "h":
|
||||
// Decrease Gain
|
||||
current := m.audioPlayer.GetUserGain(u.ID, m.eqBandIdx)
|
||||
m.audioPlayer.SetUserGain(u.ID, m.eqBandIdx, current-1.0)
|
||||
|
||||
case "up", "k":
|
||||
m.eqBandIdx--
|
||||
if m.eqBandIdx < 0 {
|
||||
m.eqBandIdx = 4
|
||||
}
|
||||
|
||||
case "down", "j":
|
||||
m.eqBandIdx++
|
||||
if m.eqBandIdx > 4 {
|
||||
m.eqBandIdx = 0
|
||||
}
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
@@ -1412,14 +1437,85 @@ func (m *Model) renderUserView() string {
|
||||
"--- Audio Settings ---",
|
||||
fmt.Sprintf("%s %d%%", labelStyle.Render("Volume:"), int(vol*100)),
|
||||
fmt.Sprintf("%s %s", labelStyle.Render("Local Mute:"), muteStr),
|
||||
}
|
||||
|
||||
// EQ Visualization
|
||||
var eqGraph []string
|
||||
if m.audioPlayer != nil {
|
||||
bands := m.audioPlayer.GetEQBands(u.ID)
|
||||
if len(bands) > 0 {
|
||||
eqGraph = append(eqGraph, "", "--- Interactive Equalizer ---", "")
|
||||
|
||||
// Render bars for 5 bands
|
||||
// 0: Bass, 1: Low-Mid, 2: Mid, 3: High-Mid, 4: High
|
||||
labels := []string{"100Hz", "350Hz", "1kHz", "3kHz", "8kHz"}
|
||||
|
||||
for i, val := range bands {
|
||||
if i >= len(labels) {
|
||||
break
|
||||
}
|
||||
|
||||
// Get current gain setting
|
||||
gain := m.audioPlayer.GetUserGain(u.ID, i)
|
||||
|
||||
// Scale 0.0-1.0 to bars (width 20)
|
||||
const maxBars = 20
|
||||
bars := int(val * maxBars)
|
||||
if bars > maxBars {
|
||||
bars = maxBars
|
||||
}
|
||||
|
||||
// Bar characters: █ ▇ ▆ ▅ ▄ ▃ ▂
|
||||
barStr := ""
|
||||
if bars > 0 {
|
||||
barStr = strings.Repeat("█", bars)
|
||||
}
|
||||
|
||||
// Colorize based on intensity
|
||||
barStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("39")) // Blue default
|
||||
if val > 0.8 {
|
||||
barStyle = barStyle.Foreground(lipgloss.Color("196")) // Red clipping
|
||||
} else if val > 0.5 {
|
||||
barStyle = barStyle.Foreground(lipgloss.Color("208")) // Orange high
|
||||
} else if val > 0.2 {
|
||||
barStyle = barStyle.Foreground(lipgloss.Color("46")) // Green normal
|
||||
}
|
||||
|
||||
// Selection Indicator
|
||||
selector := " "
|
||||
labelStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("240")).Width(6)
|
||||
gainStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("245")).Width(6)
|
||||
|
||||
if i == m.eqBandIdx {
|
||||
selector = "->"
|
||||
labelStyle = labelStyle.Foreground(lipgloss.Color("226")).Bold(true) // Yellow for selected
|
||||
gainStyle = gainStyle.Foreground(lipgloss.Color("226"))
|
||||
}
|
||||
|
||||
gainStr := fmt.Sprintf("%+3.0fdB", gain)
|
||||
|
||||
line := fmt.Sprintf("%s %s %s | %s",
|
||||
selector,
|
||||
labelStyle.Render(labels[i]),
|
||||
gainStyle.Render(gainStr),
|
||||
barStyle.Render(fmt.Sprintf("%-20s", barStr)))
|
||||
|
||||
eqGraph = append(eqGraph, line)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
info = append(info, eqGraph...)
|
||||
info = append(info,
|
||||
"",
|
||||
"--- Menu ---",
|
||||
"1. Poke",
|
||||
"2. Toggle Local Mute",
|
||||
"+/-: Adjust Volume",
|
||||
"Arrows: Adjust EQ",
|
||||
"",
|
||||
"(Press ESC to close)",
|
||||
}
|
||||
)
|
||||
|
||||
return lipgloss.JoinVertical(lipgloss.Left, info...)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user