Skip to content

Add visualizer@v1 with beats, peaks, pitch, and frequency#259

Draft
maximmaxim345 wants to merge 15 commits into
mainfrom
feat/visualizer-v1-support
Draft

Add visualizer@v1 with beats, peaks, pitch, and frequency#259
maximmaxim345 wants to merge 15 commits into
mainfrom
feat/visualizer-v1-support

Conversation

@maximmaxim345
Copy link
Copy Markdown
Member

Upgrades the TUI visualizer from the old _draft_r1 draft to the newer visualizer@v1 role, rendering everything the server streams time-aligned to the playhead: a beats strip with estimated BPM and downbeats, a peaks strip of energy onsets, and pitch and dominant-frequency (f_peak) cursors over the spectrum.

Only data that is sent by the server is displayed. For example, Music Assistant 2.9 does not support pitch.

Shouldn't be merged until Music Assistant 2.9

Still marked as draft since this requires the server to support visualizer@v1 (Sendspin/spec#86). The current stable version of Music Assistant (2.8) only supports visualizer@_draft_r1. Once 2.9 releases we can merge this.

Try it

uvx --from git+https://github.com/Sendspin/sendspin-cli.git@feat/visualizer-v1-support sendspin

Press v in the TUI to toggle the visualizer.

Negotiates the visualizer@v1 role (downbeat-aware msg 17 frames). BeatTiming flows through BeatHandler, BeatState, and the render_beat_strip pipeline; downbeats render as a filled square while regular beats stay as filled circles. Downbeat wins when both land on the same cell. Schedule appends are deduplicated by timestamp so repeated batches don't paint the same beat twice on the timeline.
The dominant-frequency and pitch arrows shared one cursor row; give each
its own line (f_peak above pitch) with the footer below. Each row is gated
on the visualizer type the server negotiated in stream/start, so a row is
only reserved when its data can actually arrive.
Fold the timeline strips and tonal cursor rows into one keep-priority row
budget (peaks, beats, f_peak, pitch, footer). Each strip needs its type in
the server-negotiated set, so a strip is hidden when the server won't send
it instead of reserving an always-empty row.
Show the pitch arrow and label whenever a confident readout arrives, even
if the server didn't list "pitch" in the stream/start types. f_peak and the
timeline strips stay type-gated.
Keep the pitch cursor row when "pitch" is negotiated so the arrow appearing
and vanishing no longer shifts the spectrum and f_peak rows. Show the arrow
whenever a pitch readout exists, regardless of confidence.
Review findings on the visualizer feature:

- Reset and detach the peak handler on disconnect and shutdown, and clear
  the peak strip, matching the beat handler. A reconnect no longer fires a
  stale asyncio timer or stale peaks against the new stream.
- Gate BeatState.is_active on the decaying pulse instead of "a beat ever
  landed", so the high-rate visualizer refresh idles when paused or silent.
- Drop the last-seen loudness/pitch/f_peak on stream/clear and stream/end so
  a stale readout from one stream can't ride into the next.
- Remove the now-unused pitch_confidence plumbing left after the confidence
  gate was dropped.
The beat strip used to be the only carrier of the "now" cursor, so a stream
with peaks but no beats had no time reference. Draw the center playhead on
the peak strip too, colored to match the beat strip's.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant