Files
ytplayer/plans/jamesdsp-audit.md
2026-10-09 19:42:11 +08:00

5.2 KiB
Raw Blame History

JamesDSP integration audit

Date: 2026-10-09
Base: 933e920 (main, Phase 0–5, offline completion, UI and transport integration)

Audio ownership found in the current runtime

Concern Current owner / finding Integration consequence
Realtime context EQ in frontend/app.js is the only runtime constructor (new AC() around line 5532). Preserve this exact lazy creation point. JamesDSP may not instantiate another context.
Media element sources EQ.attach() is the only runtime createMediaElementSource() call (around line 5559); it records sources by element in st.sources. Extract this into one AudioGraph owner with a retained WeakMap/Map. Never retry creation for a previously attached media element.
Playback elements Player.master, Player.secondary, and Player.soundEl change between els.video and els.audio for dual/audio-only playback and handoffs. EQ.apply() attaches both video and audio elements. Attach each element once; route both through the same selected insert. Do not tie graph lifetime to a track or to Player.master.
Continuity and transitions audio-continuity.js owns play/resume/alignment. Transition changes the active master and uses a bridge element; the bridge is not currently connected to EQ. Keep continuity and bridge timing unchanged. A JamesDSP route applies to the same player elements as the Standard route; do not source the bridge or alter transition policy.
iOS live processing EQ.available() is false on iOS unless data.settings.liveAudioIOS === true (around line 5516). EqRender prepares saved audio with a 32 kHz OfflineAudioContext. Gate live JamesDSP on the same setting and supported realtime graph. V1 does not process EqRender output; a DSP graph must never be layered over an already-rendered Standard EQ copy.
Level / limiter Level measures at 22.05 kHz offline and applies dB gain through EQ.setLevel(). The realtime Standard chain ends in a compressor limiter. Preserve the Level measurement and dB value. In JamesDSP mode map it into the engine gain with a documented combined-gain clamp; Standard remains untouched.
EqRender EqRender renders cached media through an offline graph at 32 kHz and saves eq-renders OPFS copies. It is independent from the single realtime AudioContext. Leave this renderer and its bytes unchanged. Until offline worklet rendering is proven, JamesDSP does not claim to process saved/rendered copies.
VocalReducer / widener Loaded lazily by feature:vocal-reducer and feature:eq-core, then connected in EQ.build(). In JamesDSP mode disconnect the Standard insert. Do not create either lazy Standard effect solely to initialize JamesDSP.
Cold path audio-continuity.js, eq-parameters.js, and app.js are core assets. JamesDSP has no existing runtime assets. Keep all new vendor/worklet/wasm/controller files exclusively in lazy feature:jamesdsp; the small graph owner may be core only if unavoidable, and its cost must be checked against integration cold-start medians.

Seam and compatibility decisions

Phase 2 will add a small frontend/audio-graph.js core seam because it gives the one-context/one-source invariant an independently testable owner. It will create the context only when the existing EQ asks for it, retain each element source for the page lifetime, and expose controlled Standard and optional JamesDSP route operations. It will not change playback, transition, or volume code. The new core request is a measured risk: compare cold FCP/bytes to plans/integration-report.md and inline the seam into an existing core module if it causes a regression.

The Standard graph's node order, filter constants, gain automation, lazy-load triggers, and destination routing are a frozen compatibility contract. Phase 2 will add a graph trace test comparing the old route with the extracted route, plus deterministic sample-response tests for the Standard chain. JamesDSP activation is opt-in and serialized: restore Standard before destroying a failed node, never create a replacement until the previous 128 MiB node is destroyed, and reject the engine when the existing context rate is not 44.1 or 48 kHz. The existing context is never closed/recreated to request 48 kHz.

The standalone port verifies 44.1/48 kHz, a fixed 128 MiB heap, immediate exact bypass, and configuration that can pause worklet rendering. It has no Safari/iPhone verification. V1 will report JamesDSP live processing unavailable on iOS unless liveAudioIOS is on; EqRender/offline rendered playback remains Standard. Text effects (Liveprog, DDC, arbitrary text EQ) will be neither shown nor accepted by the v1 settings/import surface. IR state is memory-only because JSON presets do not contain its PCM.

Licensing and release boundaries

The standalone source is pinned by commit in the vendor VERSION file and its supplied notices are retained. Its upstream Main/LICENSE contains GPL-2.0 text while the port documents GPL-3.0-or-later additions; this discrepancy is recorded and no license file will be silently rewritten. A redistribution decision remains an owner release matter. The user instruction prohibits pushing or deploying, so Phase 7 will prepare beta listening notes only and will not publish a beta build.