Commit Graph
4 Commits
Author SHA1 Message Date
OmikronApexandClaude Fable 5 9d0963d6d5 feat(audio): getLatencyBreakdown — one owner for every latency term
Deep-read §5: latency had three unreconciled truths — getLatencyMs' static
half-capacity ring guess (42.7 ms), the verifier's input-delta-only offset,
and the renderer bus adding prime+fill+resample that no figure surfaced.

New engine API + export: per-term breakdown (deviceBufferMs, input/output
driver latency, MEASURED split-ring residency, monitor total) plus the
renderer-bus song-audio delay (measured bus fill) as its own term. On the
user's split exclusive setup the measured ring sits at ~10 ms — the legacy
figure overstated monitor latency by ~33 ms (102.7 reported vs ~70 real).

getLatencyMs is unchanged for compatibility; UI adoption of the breakdown
is renderer follow-up. Snapshots regenerated (104 exports).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 03:05:19 +02:00
OmikronApexandClaude Fable 5 88f881dd1e fix(audio): refcounted monitor-mute arbiter (TLC Part II §2)
The old single monitorMuted atomic had five writers fighting
last-writer-wins: the settings checkbox, startup restore, the executor's
preload read-force-restore, releaseRoute's unconditional setMonitorMute(true)
(which clobbered the user's persisted preference), and the renderer's
song-load suppression (un-refcounted — overlapping windows un-suppressed
each other early).

Native arbiter on SourceChain: userMonitorMute (the preference — checkbox +
restore only), refcounted monitorMuteHolds (force-mute overrides), and
refcounted suppressions (setMonitorMuteSuppressed keeps its bool surface;
true=acquire, false=release, clamped at 0). Effective dry-mute =
(holds || pref) && chain empty && no suppression — the suppressed-beats-muted
precedence is unchanged. New exports: acquire/releaseMonitorMuteHold,
getMonitorMuteState (diag); snapshots regenerated.

Executor rewrite: acquires a suppression (dry-during-load, the default) or a
hold, and releases exactly what it acquired via a single-fire closure that
runs UNCONDITIONALLY (each load owns its acquisition — the stale-snapshot
race against a mid-hold user toggle is structurally gone). releaseRoute no
longer touches mute state at all. The ownership test now pins: preference
API never called, acquire/release balanced.

Renderer callers are unchanged: the checkbox writes the preference as
before, and the song-load suppression sites now compose instead of racing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 03:01:09 +02:00
OmikronApexandClaude Fable 5 db337eaf29 fix(audio): serialize chain mutations + chainGeneration (phase 7a)
The single highest-value fix of the TLC pass (deep-read §1): one native
chain-mutation mutex (addon/ChainOps) held across the FULL Execute() of
every chain worker (LoadPreset/LoadVST/LoadNAM/LoadIR/ReplaceIR) and the
synchronous mutators (clearChain/removeProcessor/moveProcessor). Two
overlapping loadPreset calls can no longer interleave clear()/addProcessor()
into a merged-garbage chain — the plugin-vs-plugin fight becomes
last-writer-wins.

chainGeneration (monotonic, bumped under the mutex) is returned in loadPreset
results and exposed as getChainGeneration (new export, snapshot regenerated),
so the audio-effects executor can detect a foreign write invalidated its
stageSlots map and re-sync instead of flipping bypass/params on wrong slots —
the prerequisite for the single-chain-owner ownership track.

Also rides here: LoadPresetWorker's slot-state restore goes through
setSlotState() instead of const_cast (deep-read §9).

The phase-0 storm test flips from expected-fail to a hard gate: 50 iterations
of concurrent loadPreset now always end with exactly one caller's chain.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 01:45:01 +02:00
OmikronApexandClaude Fable 5 bbb3b58db8 test(contracts): Phase 0.a contract snapshots for audio surface
Snapshot the three public surfaces the audio-engine decomposition must not
change (docs/audio-engine-tlc.md Part IV §4): addon export table, audio-bridge
IPC channels, preload audio/audioEffects API keys. contract-check.test.js
diffs regenerated surfaces against the committed snapshots.

result-shapes.json (golden result key/type shapes) is deferred until the
engine_units harness can run the addon against a null device.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 23:21:21 +02:00