aboutsummaryrefslogtreecommitdiff
path: root/docs/design/2026-07-03-audio-panel-spec.org
diff options
context:
space:
mode:
Diffstat (limited to 'docs/design/2026-07-03-audio-panel-spec.org')
-rw-r--r--docs/design/2026-07-03-audio-panel-spec.org160
1 files changed, 0 insertions, 160 deletions
diff --git a/docs/design/2026-07-03-audio-panel-spec.org b/docs/design/2026-07-03-audio-panel-spec.org
deleted file mode 100644
index 16a087d..0000000
--- a/docs/design/2026-07-03-audio-panel-spec.org
+++ /dev/null
@@ -1,160 +0,0 @@
-#+TITLE: Audio Panel — the pulsemixer console
-#+AUTHOR: Craig Jennings
-#+DATE: 2026-07-03
-#+TODO: TODO | DONE
-#+TODO: DRAFT READY DOING | IMPLEMENTED SUPERSEDED CANCELLED
-
-* IMPLEMENTED Status
-:PROPERTIES:
-:ID: 71f556c6-ee02-47cc-a3be-68c8289380f3
-:END:
-- [2026-07-03 Fri] IMPLEMENTED — built in a no-approvals speedrun in the
- dotfiles repo (branch panel-bugfixing): engine (pactl), presenter, GTK
- panel, PTT arming, bar indicator, and the bar/keybind wiring, across four
- commits 65e5bb0..9601420. 102 unit tests + a passing AT-SPI smoke on velox.
- All five Decisions below resolved. Live-eyeball validation (visual polish,
- PTT-in-a-meeting, fader feel, the master-mute hardware key) is the one open
- follow-up, tracked as a manual-testing task in todo.org.
-- [2026-07-03 Fri] DRAFT — stub from the todo.org task "Audio panel spec"
- (roam ask 2026-07-02) plus the 2026-07-03 waybar/sound design discussion.
- Written to iterate alongside the prototype
- (=working/sound-panel/sound-panel-prototype.html=). Spine is present; the
- Decisions and Design detail get filled in as we go.
-
-* Metadata
-
-| Field | Value |
-|--------+---------------------------------------------------|
-| Status | implemented |
-|--------+---------------------------------------------------|
-| Owner | Craig Jennings |
-|--------+---------------------------------------------------|
-| Repo | dotfiles |
-|--------+---------------------------------------------------|
-| Kin | net panel + bt panel (architecture + aesthetic |
-| | donors), desktop-settings panel (sibling) |
-|--------+---------------------------------------------------|
-
-* Problem
-
-Audio control today is the pyprland audio scratchpad (Super+A) — a floating
-pulsemixer TUI — plus scattered bar affordances: =pulseaudio= (volume, click
-to mute sink), =pulseaudio#mic= (mic glyph + mic-toggle), Super+M audio-cycle
-ring, Super+Shift+A mic-toggle. There's no single glanceable surface that
-shows every sink and source, lets you set the default output/input, and
-carries the meeting-grade mic controls Craig wants (a clean muted mode and a
-hold-to-talk mode). The net + bluetooth panels set the pattern for exactly
-this shape; audio is the third instrument in the family.
-
-* Goals
-
-1. One panel, opened from the bar's sound glyph, exposing the full pulsemixer
- surface: every sink and source, per-device volume, per-device mute, and
- switching the default output and input.
-2. Replace the pyprland audio scratchpad (Super+A) as the primary audio UI.
-3. Mic modes for meetings: *live*, *muted*, and *push-to-talk* (mic stays
- muted except while Space is held, releasing re-mutes).
-4. A *master quick-mute* — one action mutes all output — reachable from the
- faceplate and a keybind.
-5. Instrument-console aesthetic and architecture consistent with net + bt:
- same faceplate, lamps, engraved sections, console keys, needle gauges,
- verify-everything contract.
-6. The bar glyph reflects live state: speaker + three arcs normally, a
- speaker-with-✕ when muted (Craig's called glyphs).
-
-* Design sketch
-
-Prototype: =working/sound-panel/sound-panel-prototype.html= (the reference for
-layout + idioms below).
-
-** Surface (from the prototype)
-
-- *Faceplate* — status lamp, sound glyph, state word (PLAYBACK / MUTED), a
- MUTED badge, the SND·01 unit label, and the *master quick-mute switch*
- (same switch idiom as net wifi / bt power), plus the close ✕.
-- *OUTPUTS section* — one row per sink. Row body click = set default (gold
- DEF tag). A machined fader sets that sink's volume; the trailing glyph
- mutes just that device. Active/default row is emphasized (cream name, gold
- lamp/glyph).
-- *INPUTS section* — one row per source, same idioms.
-- *Mic mode* — three console keys: LIVE / MUTED / PUSH·TALK. Push-to-talk
- keeps the mic muted (red IN needle) and un-mutes only while Space is held.
-- *Twin VU needles* — output level + input level, the sound analog of net
- throughput and bt battery gauges. Needle goes red when its side is muted.
-
-** Architecture — clone the net/bt panel stack
-
-- GTK4 + gtk4-layer-shell, Blueprint =.blp= → committed =.ui= (=make ui=,
- dev-only build dep).
-- Humble-object split: a GTK-free PanelModel presenter (unit-tested like the
- net/bt PanelModels) + thin composite-widget pages. Backing actions in a
- GTK-free =audio.py= that shells to the audio control layer (pactl /
- wpctl / pulsemixer — pick below), TDD'd with fake binaries.
-- One gated AT-SPI smoke (=run-panel-smoke.sh= pattern).
-- Shared instrument-console palette CSS asset (the one net/bt/settings all
- load) — do not duplicate the palette block.
-- Code lives in dotfiles =audio/= sibling to =net/= (src-layout, tests in
- =tests/audio/=).
-
-* Decisions (Craig)
-
-** DONE Audio control backend — pactl vs wpctl vs pulsemixer
-CLOSED: [2026-07-03 Fri]
-Resolved: =pactl= (the engine module is =pactl.py=). Both ratio and velox run
-PipeWire with the pipewire-pulse compat layer and no PulseAudio daemon, so
-pactl and wpctl hit the same graph — but =pactl -f json= gives structured,
-name-addressable output where wpctl offers only a volatile-id tree. Reads go
-through =pactl -f json list sinks|sources= + =get-default-*=; writes target
-devices by stable name behind an argv-charset guard.
-
-** DONE Push-to-talk mechanism under Wayland (feasibility — phase 1)
-CLOSED: [2026-07-03 Fri]
-Resolved: route (a), Hyprland dynamic binds. The phase-1 spike confirmed all
-three primitives on velox (Hyprland 0.55.4): =hyprctl keyword bind/unbind=
-adds and removes a bind live, =bindr= fires on release, and =pactl
-set-source-mute @DEFAULT_SOURCE@ 0|1= toggles the mic cleanly. =ptt.py= arms a
-press bind (un-mute) + a bindr (re-mute) on entering PTT mode and unbinds on
-leaving, so the talk key isn't grabbed globally otherwise. No evdev needed.
-Documented behavior: while PTT is armed, the talk key is the talk key.
-
-** DONE Quick-mute keybind + scope
-CLOSED: [2026-07-03 Fri]
-Resolved: the XF86AudioMute hardware key (Super+Shift+M turned out to be taken
-by the monocle-layout bind, so the spec's assumption was wrong). The mute key
-now runs =audio quick-mute=, which mutes every output (master), not just the
-default sink — identical on a single-sink machine, correct on a multi-sink
-one. Also reachable from the faceplate master switch and the panel. Scope:
-master mute of all sinks, with verify-after-apply per sink.
-
-** DONE Bar glyph click map
-CLOSED: [2026-07-03 Fri]
-Resolved with the low-regret wiring: kept the existing =pulseaudio= waybar
-module (left-click mute, scroll volume — no regression) and repointed its
-right-click from the retired pulsemixer scratchpad to =audio-panel=. So: left
-= mute, right = open panel, scroll = volume. A fuller =custom/audio= indicator
-(state-following speaker glyph + its own click map) is built and tested
-(=indicator.py= + =waybar-audio=) but stays unwired until the new bar glyph
-gets a live eyeball — the swap is a one-line waybar edit when Craig's ready.
-
-** DONE Fate of the existing audio affordances
-CLOSED: [2026-07-03 Fri]
-Resolved: Super+A repurposed from =pypr toggle audio= (the pulsemixer
-scratchpad) to =audio-panel= — the panel is the primary audio UI now, so the
-scratchpad is retired. Its definition still sits in the machine-local
-=pyprland.toml= (not stowed) and can be deleted by hand. Kept: =pulseaudio= +
-=pulseaudio#mic= waybar modules (glance + scroll + the mic-mute glance),
-Super+M cycle, Super+Shift+A + XF86AudioMicMute mic-toggle. Changed:
-XF86AudioMute → master quick-mute (see the quick-mute decision above).
-
-* Implementation phases
-
-1. Push-to-talk feasibility spike (decision above) — the one unknown; settle
- the mechanism before committing the mic-mode design.
-2. =audio.py= backings (list/get/set/mute/default for sinks + sources) —
- pure engine, TDD with a fake audio backend.
-3. PanelModel presenter (rows, default tracking, mic modes, master mute,
- verify-after-apply) — unit-tested, no GTK.
-4. Blueprint UI + sound bar glyph (normal / muted / ptt states) + open/close
- wiring; shared palette css; AT-SPI smoke.
-5. Bar-affordance consolidation per the decision above; retire the Super+A
- scratchpad; keybinds.