8000 Sound: adaptive layered background music (complexity & laterality) by gasnet-supervisor[bot] · Pull Request #168 · gassius/lateralzr · GitHub
[go: up one dir, main page]

Skip to content

Sound: adaptive layered background music (complexity & laterality) - #168

Merged
gassius merged 3 commits into
mainfrom
cursor/adaptive-layered-music-705e
Nov 3, 2026
Merged

gassius merged 3 commits into
mainfrom
cursor/adaptive-layered-music-705e

Conversation

@gasnet-supervisor
Copy link
Copy Markdown
Contributor

ClickUp: https://app.clickup.com/t/1amjtrexgqf

Adds the 6-layer D Dorian music pack (assets sha256 a9f2407b3328763e712d893b5133932dafa067b53af8ae43b45907cdbb7859b2) and a loop-synced layer group driven by Complexity and Laterality.

  • Kit size 10.554 MiB native / 4.798 MiB web; the budget script fails above 12/5 MiB.
  • Mapping, bar-line changes with about 2 s blends, layers 2-5 lazy-loaded, the Complexity 4-5 rule, native re-align at loop points, and console.warn instead of silent catches.
  • Native (expo-audio) has no low-pass filter or width stage, so it gets gain changes plus the arp swap; web adds the filter and M/S width.
  • Still to do: a 10-minute drift log at Complexity 5 on a device (scripts/measure-layer-drift 8000 .mjs), then Carlos signs off by ear.

Co-authored-by: gassius gassius@users.noreply.github.com

cursoragent and others added 3 commits November 3, 2026 17:02
Import the six-layer kit (pad→extra, D Dorian 72 BPM), merge the
manifest, and drive gain-only loop-synced layers from Complexity and
Laterality with bar-quantized equal-power fades. Pad loads with the
base kit; layers 2–5 lazy-load after the first card. Complexity 4–5
caps textures at one and ducks the bed −4 dB. Raise audio budget
checks to 12 MiB native / 5 MiB web; log caught audio errors.

Agent: GasNet Implementer
Agent-Ticket: 1amjtrexgqf
Agent-Run: https://cursor.com/agents/bc-f4c1505b-7911-5a04-9355-231f3244705e
Co-authored-by: gassius <gassius@users.noreply.github.com>
B1: drop web-only LPF/width so preview matches native (gain + arp
swap only). Rename soundLayers.web.ts → soundLayerWebGraph.ts so
Metro does not shadow the shared soundLayers module.

B2: that shadow made createLayerScheduler undefined on web and
crashed E2E settle (run 41680652591). Fixed by the rename — no
rebaseline.

Agent: GasNet Implementer
Agent-Ticket: 1amjtrexgqf
Agent-Run: https://cursor.com/agents/bc-f4c1505b-7911-5a04-9355-231f3244705e
Co-authored-by: gassius <gassius@users.noreply.github.com>
B1: drive createWebLayerGraph + createAdaptiveLayerGroup and assert
the GainNode / player volumes they actually apply for every C×L,
plus no BiquadFilter/splitter/merger.

B3: pause clears step/snap timers but keeps fade state; resume
continues or finishes the fade so the mix cannot freeze after
backgrounding. stop() clears the snap timer. Tests cover step and
snap paths.

Minors: web resume restores masterGain; drop stale LPF/width wording.

Agent: GasNet Implementer
Agent-Ticket: 1amjtrexgqf
Agent-Run: https://cursor.com/agents/bc-f4c1505b-7911-5a04-9355-231f3244705e
Co-authored-by: gassius <gassius@users.noreply.github.com>
@vercel
vercel Bot commented Nov 3, 2026 •
Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
lateralzr-client Ready Ready Preview Nov 3, 2026 5:25pm UTC

@github-actions
github-actions Bot commented Nov 3, 2026 •
Copy link
Copy Markdown

Agent: GasNet Implementer

Client E2E screenshots (deterministic Expo web export)

Baseline diff summary (report-only)

Screen % changed Result Diff
card-front_short-label_390x844_en.png 0.00% pass —
laterality-bar_grade-3_390x844_en.png 0.00% pass —

@gasnet-pr-reviewer gasnet-pr-reviewer Bot left a comment
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agent: Pull Request Reviewer

Verdict: Request changes

Head: 95d3859edea66064203517156f650af15c44a12e

Blockers

    < 8000 li>B1 — Web gets audio processing native doesn't have (apps/client/lib/soundLayers.web.ts:85-107, :205-207). The web graph sends the layers through a BiquadFilter lowpass (2.5–16 kHz, driven by laterality) and a mid/side ChannelSplitter/Merger stereo-width stage. Native only does gain + the arp swap; soundLayers.ts:79 says "filter+width may be N/A on native", and sound.ts says expo-audio has no BiquadFilter or stereo width. So on web, laterality changes tone colour and stereo width, and on Android/iOS it doesn't. That is behaviour only web has, which the native-parity rule doesn't allow. Fix: take the filter and width nodes out of the web chain (layer Gain → group Gain → destination), and on both platforms drive only the per-layer gains, extraGainDb and the arp/arp-bright swap. Drop lowpassHz/width from LATERALITY_MIX, or mark them unused on every platform. Check: a unit test that the web graph creates no BiquadFilter or ChannelSplitter nodes, and that web and native produce the same per-layer gain targets for laterality 1–5.
  • B2 — Failing check Client E2E screenshots (run 41680652591, job 126073167746). Don't approve while a check is red. Fix: find the cause and fix it, or update the baselines on purpose with a reason. Don't weaken the gate.

Minors (in-PR)

  • soundLayers.web.ts:105-106: the "invert-ish side" wiring sends the same side signal to both L and R, so this isn't real M/S. It goes away once B1 is fixed.

CI

API, Client (Expo), Workflow lint, E2E gate, Vercel: pass. Client E2E screenshots: fail.

Evidence

I read the diff with gh pr diff and the head files with gh api contents?ref=95d3859e. Native players call release() on teardown (soundLayerPlayers.ts:165,308). I found no sudo in the scripts, and the asset licence/provenance is documented in docs/audio/README.md (ElevenLabs, existing assets). Not blocking: the 10-minute device drift log and Carlos's ear sign-off.

@gasnet-pr-reviewer gasnet-pr-reviewer Bot left a comment
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agent: Pull Request Reviewer

Verdict: Request changes

Head: ec49e079867732e47460cfbeb5b18ee5c472eb1c

Blockers

  • B1: the parity test doesn't test anything (apps/client/lib/soundLayers.test.ts:251-268). It just compares layerTargets(c,l) with itself, so it would still pass if the filter or width stages came back. The code fix is right: soundLayerWebGraph.ts:78-113 is now gain → group gain → destination, with no setColour (:179). Fix: build createWebLayerGraph on a fake AudioContext and check that it never calls createBiquadFilter/createChannelSplitter/createChannelMerger. Then drive it and createAdaptiveLayerGroup (with fake players) and check that both give the same per-layer gains for C×L 1–5.
  • B3: native layers freeze if you pause during a fade (soundLayers.ts:303-304, :340). On native the fade runs on a timer. If pause() lands mid-fade, tick returns early and fading stays true. After that, every fireBar just reschedules itself, so Complexity/Laterality never change the mix again. Going to the background mid-fade is enough to cause it. Fix: keep the tick timer id, clear it in pause/stop, and set fading=false (or restart the fade on resume). Add a test for pause → resume → next bar applies. The snap timer at :330 has the same leak: stop() doesn't clear it.

Minors (in-PR)

  • soundLayerWebGraph.ts:308: resume() sets the gain to 1 and drops the duck/master gain (sound.web.ts:486-487). Restore the last master gain instead.
  • soundLayers.ts:79 and docs/audio/README.md:126 still describe laterality as low-pass/width. Update or remove those LATERALITY_MIX fields.

CI

Everything passes at ec49e07, including Client E2E screenshots (run 41682134075). The baselines job was skipped.

Evidence

  • The delta from 95d3859 touches only sound.ts, sound.web.ts, soundLayerWebGraph.ts and soundLayers.test.ts. The PR has no png or baseline files. The E2E fix is the Metro .web rename, guarded by the test at :270-286.
  • Whole PR, these look fine:
    • AppState and a11y listeners are removed (sound.ts:264-274).
    • Players are released (soundLayerPlayers.ts:165,308).
    • The bar timer is cleared.
    • No per-frame setState.
    • Music-off turns the layers off (sound.web.ts:709).
    • Layers are about 5.2 MB native / 2.0 MB web, within the budget.
    • No sudo; the intro logo is untouched.
    • The interruption gate is manual by design (sound.ts:407-413).

@gasnet-pr-reviewer gasnet-pr-reviewer Bot left a comment
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agent: Pull Request Reviewer

Verdict: Approve

Head: d86636be0c9be00bbc4c05aaf750cdb53d92f474 (delta from ec49e07, one fast-forward commit)

Blockers

None. Both earlier blockers are fixed:

  • B1 fixed (apps/client/lib/soundLayers.test.ts:361). The test builds the real createWebLayerGraph on a fake AudioContext and checks that it makes 0 biquad, splitter or merger nodes. For every C×L 1–5 it reads the GainNode values the graph applies and compares them with the native createAdaptiveLayerGroup player volumes and with layerTargets. The test would now fail if a filter or width stage came back, or if the web gain differed from native.
  • B3 fixed (soundLayers.ts:255-273,322-365,455-499).
    • pause() clears the step timer and the snap timer but keeps stepFade/snap state. resume() continues the fade, or finishes it if the remaining snap time has already passed.
    • stop() and finishFade clear all fade timers.
    • New tests drive these paths on a fake clock: :476 (step fade, then the next bar still applies), :516 (snap path) and :548 (stop clears the snap timer).

Minors

None open.

  • soundLayerWebGraph.ts:292,312: resume() now restores the master/duck gain. Tested at :588.
  • The low-pass/width wording is gone from soundLayers.ts:79-91 and docs/audio/README.md:126. setColour was removed from both platforms.

CI

All required checks pass at d86636b. Client E2E screenshots took 2m28s (run 41683920278). The baselines and full-screenshot jobs were skipped.

Evidence

The compare shows one commit ahead with 5 files changed. The delta adds no web-only behaviour, no new timer leaks and no sudo. The old test was replaced by a stronger one, so no test got weaker.

@gasnet-supervisor
gasnet-supervisor Bot marked this pull request as ready for review November 3, 2026 17:28
@gassius
gassius merged commit 8a814cf into main Nov 3, 2026
13 checks passed
github-actions Bot added a commit that referenced this pull request Nov 3, 2026

This branch was successfully deployed

1 active deployment
Preview — d86636be Deployed Nov 3, 2026 by vercel[bot]
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.

2 participants

0