init commit
This commit is contained in:
133
internal/ui/waveform/waveform.go
Normal file
133
internal/ui/waveform/waveform.go
Normal file
@@ -0,0 +1,133 @@
|
||||
package waveform
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
|
||||
"playback/internal/audio"
|
||||
"playback/internal/ui"
|
||||
)
|
||||
|
||||
// Model represents the waveform visualization component
|
||||
type Model struct {
|
||||
Width int
|
||||
Height int
|
||||
Focused bool
|
||||
Samples []float64
|
||||
Position float64 // 0.0 to 1.0
|
||||
Duration time.Duration
|
||||
}
|
||||
|
||||
// New creates a new waveform model
|
||||
func New() Model {
|
||||
return Model{
|
||||
Height: 3,
|
||||
}
|
||||
}
|
||||
|
||||
// SetSamples sets the waveform samples
|
||||
func (m *Model) SetSamples(data *audio.WaveformData) {
|
||||
if data != nil {
|
||||
m.Samples = data.Normalized()
|
||||
}
|
||||
}
|
||||
|
||||
// SetPosition sets the playback position (0.0 to 1.0)
|
||||
func (m *Model) SetPosition(pos float64) {
|
||||
m.Position = pos
|
||||
}
|
||||
|
||||
// SetDuration sets the total duration
|
||||
func (m *Model) SetDuration(d time.Duration) {
|
||||
m.Duration = d
|
||||
}
|
||||
|
||||
// SetSize sets the component dimensions
|
||||
func (m *Model) SetSize(width, height int) {
|
||||
m.Width = width
|
||||
m.Height = height
|
||||
}
|
||||
|
||||
// SetFocused sets the focus state
|
||||
func (m *Model) SetFocused(focused bool) {
|
||||
m.Focused = focused
|
||||
}
|
||||
|
||||
// View renders the waveform
|
||||
func (m Model) View() string {
|
||||
contentWidth := m.Width - 4 // Account for border and padding
|
||||
|
||||
if contentWidth < 10 {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Render waveform with needle position
|
||||
left, needle, right := RenderWithColors(m.Samples, contentWidth, m.Position)
|
||||
|
||||
// Apply styles
|
||||
waveformLine := lipgloss.JoinHorizontal(
|
||||
lipgloss.Left,
|
||||
ui.BaseStyle.Render(left),
|
||||
ui.NeedleStyle.Render(needle),
|
||||
ui.BaseStyle.Render(right),
|
||||
)
|
||||
|
||||
// Time markers
|
||||
startTime := "00:00"
|
||||
endTime := formatDuration(m.Duration)
|
||||
currentTime := formatDuration(time.Duration(m.Position * float64(m.Duration)))
|
||||
|
||||
timeMarkerWidth := contentWidth - len(startTime) - len(endTime)
|
||||
if timeMarkerWidth < 0 {
|
||||
timeMarkerWidth = 0
|
||||
}
|
||||
|
||||
// Calculate current time position
|
||||
currentTimePos := int(m.Position * float64(contentWidth))
|
||||
currentTimeWidth := len(currentTime)
|
||||
|
||||
// Build time marker line
|
||||
timeLine := ui.TimestampStyle.Render(startTime)
|
||||
|
||||
// Position current time
|
||||
spaceBefore := currentTimePos - len(startTime) - currentTimeWidth/2
|
||||
if spaceBefore < 0 {
|
||||
spaceBefore = 0
|
||||
}
|
||||
spaceAfter := contentWidth - len(startTime) - spaceBefore - currentTimeWidth - len(endTime)
|
||||
if spaceAfter < 0 {
|
||||
spaceAfter = 0
|
||||
}
|
||||
|
||||
timeLine = lipgloss.JoinHorizontal(
|
||||
lipgloss.Left,
|
||||
ui.TimestampStyle.Render(startTime),
|
||||
lipgloss.NewStyle().Width(spaceBefore).Render(""),
|
||||
ui.NeedleStyle.Render(currentTime),
|
||||
lipgloss.NewStyle().Width(spaceAfter).Render(""),
|
||||
ui.TimestampStyle.Render(endTime),
|
||||
)
|
||||
|
||||
content := lipgloss.JoinVertical(
|
||||
lipgloss.Left,
|
||||
waveformLine,
|
||||
timeLine,
|
||||
)
|
||||
|
||||
// Apply border style based on focus
|
||||
style := ui.WaveformStyle
|
||||
if m.Focused {
|
||||
style = ui.WaveformFocusedStyle
|
||||
}
|
||||
|
||||
return style.Width(m.Width - 2).Render(content)
|
||||
}
|
||||
|
||||
func formatDuration(d time.Duration) string {
|
||||
d = d.Round(time.Second)
|
||||
m := int(d / time.Minute)
|
||||
s := int((d % time.Minute) / time.Second)
|
||||
return string(rune('0'+m/10)) + string(rune('0'+m%10)) + ":" +
|
||||
string(rune('0'+s/10)) + string(rune('0'+s%10))
|
||||
}
|
||||
Reference in New Issue
Block a user