Skip to content

feat: per-app output routing with per-device volume - #18

Merged
pantafive merged 2 commits into
mainfrom
feature/per-app-routing
Jun 9, 2026
Merged

pantafive merged 2 commits into
mainfrom
feature/per-app-routing

Conversation

@pantafive

Copy link
Copy Markdown
Owner

Summary

Drag one or more output devices onto an app to pin its audio to them; the rest of the system stays on the default output. Each pinned device carries its own volume slider, so one app can play to several outputs at independent levels. A pinned device leaves the output picker — it is owned by that app.

The per-app tap aggregate fans out to every present pin (stacked, drift-compensated). The IO proc was confirmed live to fire on a multi-sub-device tap aggregate, with HAL mirroring the stream, so the gain render is unchanged. A routed tap renders transparent and each device's own VirtualMainVolume is the volume — one DeviceVolumeController apiece, the same control multi-output members use. An absent pin drops out and re-applies when the device returns.

AppVolume now stores a clock-first UID list; a custom decoder folds the legacy single-UID blob in so saved settings survive the upgrade. A routed app row swaps its master slider for an accent card of per-device rows.

The site popover mockup and README mirror the feature.

Test plan

  • make build and make test pass; the volume store round-trip and legacy single-UID decode are unit-tested.
  • Route a playing app to two connected devices: both play, each slider moves only its own device, the speaker icon mutes just that device.
  • Drop the last pin: the device returns to the picker and the app falls back to the default.
  • Settings written by a prior single-route build still load.

pantafive added 2 commits June 9, 2026 01:42
Drag one or more output devices onto an app to pin its audio there while
the rest of the system stays on the default output. A pinned device
leaves the picker — it is owned by that app — and each pinned device
carries its own volume slider, so one app can play to several outputs at
independent levels.

- AppVolume stores a clock-first UID list; a custom decoder folds the
  legacy single-UID blob in so saved settings survive the upgrade.
- The tap aggregate fans out to every present pin (stacked, drift-
  compensated, IO proc proven to fire); a routed tap renders transparent
  and each device's own VirtualMainVolume is the volume, one
  DeviceVolumeController apiece.
- An absent pin drops out and re-applies on return; reconcile rebuilds
  only the taps whose device set moved.
- A routed app row swaps its master slider for an accent card of
  per-device rows; a grab pointer marks draggable device rows, the app
  list gains an APPS label, and routed-but-silent rows stay legible.
Carry the per-app output feature into the marketing surfaces: a README
line and a site prose section, plus a working popover mockup. Drag a
device onto an app to pin it; several devices stack into an accent card
with a volume slider each, and a pinned device leaves the picker. The
routed-app rows mirror the real popover.
@pantafive
pantafive merged commit 8ab6b4b into main Jun 9, 2026
2 checks passed
@pantafive
pantafive deleted the feature/per-app-routing branch June 9, 2026 07:47
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