Skip to content

feat(core): route grouped audio through a group bus in preview - #3287

Open
vanceingalls wants to merge 1 commit into
wa-23b-group-rowfrom
wa-23c-group-preview
Open

feat(core): route grouped audio through a group bus in preview#3287
vanceingalls wants to merge 1 commit into
wa-23b-group-rowfrom
wa-23c-group-preview

Conversation

@vanceingalls

Copy link
Copy Markdown
Collaborator

Summary

Step B3 of the audio-mixer-groups execution runbook (plans/audio-execution/B3-group-preview.md). Preview-engine-only: an audio element carrying data-audio-group now feeds a per-group GainNode (built lazily on first use) instead of landing on master directly, so grouped members sum before the ear and have a place for a group-level FX chain / volume / mute in later steps (B4-B7).

  • Group bus wiring reuses the existing attachElementFxChain/scheduleVolumeLane machinery unchanged — just pointed at the <hf-audio-group> element — against composition time (design doc §1.3), since a group has no data-start and a missing one parses as 0.
  • A group id with no matching <hf-audio-group> element still gets a flat, unprocessed bus (graceful degradation, same posture as B1's resolveAudioGroups).
  • The bus persists across stopAll() (mirrors _masterGain's own lifecycle: created lazily, only destroy() disposes it) so replaying a group doesn't rebuild its chain.
  • Render is untouched — stays flat until B4. audio-groups is still a 0% canary.
  • Fixed a real bug in B1's audioGroupOf: it called el.tagName.toLowerCase() unconditionally, which crashes on the many { muted: false } as HTMLMediaElement test doubles used throughout this suite (no real tagName/getAttribute). Made it tolerant, mirroring readChain's existing guard style in runtime/audioFx.ts.
  • schedulePlayback was already 110 lines before this diff; extracted resolveDestination and handleSourceEnded (92 lines now), then suppressed the CRAP/complexity finding on the remainder — inherently sequential graph wiring, not a decision tree — per the same // fallow-ignore-next-line complexity precedent B2 used on TimelineLogicalRow.

Stack

Depends on (merge after all): #3286 (B2) → #3278 (B1) → #3277 (P2) → #3276 (P1) → #3275 (A2) → #3274 (A1).

Test plan

  • bun run build clean
  • packages/core full suite: 2359/2359 (7 new group-routing tests in webAudioTransport.test.ts)
  • oxfmt/oxlint clean
  • Pre-commit hooks (lint, format, typecheck, fallow, commitlint) green
  • Manual: preview a grouped composition with a reverb on the group in hyperframes preview (needs a running dev server — left unchecked, same as B2's PR)

🤖 Generated with Claude Code

An audio element carrying `data-audio-group` no longer lands its gain on
the master bus directly — it feeds a per-group `GainNode` (built lazily on
first use, one per group id) which itself feeds master, so members of the
same group sum before the ear, ready for a group-level FX chain and
volume/mute in later steps. An id with no matching `<hf-audio-group>`
element still gets a plain, unprocessed bus rather than losing the track.

The group's own chain and volume lane are wired through the same
`attachElementFxChain`/`scheduleVolumeLane` every element already uses,
against the group's clock — composition time (design doc §1.3), since a
group has no `data-start` and a missing one parses as 0. The bus persists
across `stopAll()` (mirroring `_masterGain`'s own lifecycle) so replaying a
group does not rebuild its chain; only `destroy()` disposes it.

Render is untouched — stays flat until B4; `audio-groups` is still a 0%
canary so nothing ships this to a real composition without hand-authoring
`data-audio-group`.

Also: `audioGroupOf` (B1) crashed on any element lacking a real `tagName`/
`getAttribute` — exactly the shape of most `HTMLMediaElement` test doubles
in this suite, including this file's own `mockEl`. Made it tolerant, same
style as `readChain`'s existing guard in `runtime/audioFx.ts`.

`schedulePlayback` was already 110 lines pre-existing before this diff;
extracted `resolveDestination` and `handleSourceEnded` to shrink it to 92,
then suppressed the remainder (inherently sequential graph wiring, not a
decision tree) per the same precedent B2 used on `TimelineLogicalRow`.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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