Audio: instant bullseye cue on alignment, decoupled from the held lock
Three-way consensus (Jay/Codex/Claude): one sound, split timing not identity. The bullseye sound now means 'centered right now' and fires the instant you're aligned - even a fast pass over center - so the zone that needs the most help locating always announces itself. It no longer waits on the velocity+dwell lock, which was the delay near center. - SurfaceTickPolicy: outside the center zone, proximity ticks (unchanged anchor scheduler). Inside, emit ALIGNED every frame -> caller loops level.wav. Entry is immediate (no dwell, no velocity gate). Alignment uses SPATIAL hysteresis (enter <=0.2, rearm only after leaving >=0.35) - not a time debounce, so a legitimate quick re-crossing still announces. Ticks resume immediately on exit. - Alignment error = hypot(stable pitch, stable roll) - the SAME axes that drive the bubble, so the sound can't lag the visual (Codex's implementation note). - Player: level.wav loops while aligned (ensureLevelLooping, idempotent), stopped before every restart and on exit; pending-load queue retained. - The held-level confirmation (persistent lime, label, haptic) stays on the velocity-aware LockDetector; the locating sound is fully decoupled from it. Tests: immediate cue on fast crossing, one start / ticks-suppressed while inside, stop+resume-ticks on exit, spatial-hysteresis rearm, tick reactivity. 81 tests passing; assembleDebug clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+20
-11
@@ -12,17 +12,26 @@ controls it) and never takes audio focus.
|
||||
|
||||
Two modes / two mechanisms, one sound identity (all derived from a 500 Hz blip):
|
||||
|
||||
**SURFACE (2-D) — TONES = continuous proximity ticking.** A tick (`tick.wav`, 60 ms) repeats
|
||||
faster as you near level — axis-agnostic "warmer/colder", the RATE is the whole message (a
|
||||
bubble level asks nothing of you). **No settle gate** — it ticks at every point in the
|
||||
adjustment. Rate is exponential in the stable tilt error, clamped **1.5/s (≥~5°) → 8/s
|
||||
(≤~0.4°)**, hard-capped at 8/s. **Constant loudness** (acceleration is the signal, not volume).
|
||||
On lock (reusing `LockDetector.isLocked` — dwell + hysteresis, no second audio threshold):
|
||||
ticking stops, `level.wav` plays once, silence while it holds; unlock resumes ticking.
|
||||
Scheduling is deadline-based off a **monotonic clock** — advances a next-tick deadline (no
|
||||
drift, no catch-up bursts, ≤1 tick per sensor update); re-entry (enable TONES / return
|
||||
FACE_UP / unlock) ticks immediately. `level.wav`'s 1.39 s stream is retained and stopped on
|
||||
unlock/disable so a fresh tick can't overlap its tail.
|
||||
**SURFACE (2-D) — TONES.** One vocabulary: **faster ticks = approaching, the bullseye sound =
|
||||
centered right now.** Timing is split, not sound identity:
|
||||
|
||||
- *Outside the center zone:* `tick.wav` (60 ms) repeats faster as you near level — axis-agnostic
|
||||
"warmer/colder", rate is the message. No settle gate. Rate is exponential in the error,
|
||||
clamped **1.5/s (≥~5°) → 8/s (≤~0.4°)**, hard-capped. **Constant loudness** (acceleration is
|
||||
the signal). Scheduling is anchored to the last tick off a **monotonic clock**, due-time
|
||||
recomputed each frame from the current interval → immediate reaction to changing tilt, no
|
||||
drift, no catch-up burst, ≤1 tick per update.
|
||||
- *Inside the center zone:* the ticks give way to the looping `level.wav` **immediately — no
|
||||
dwell, no velocity gate — even on a fast pass**, because a momentary alignment is real,
|
||||
locating information (that little zone needs the most help). It stops the instant you leave.
|
||||
Alignment uses **spatial hysteresis** (enter ≤0.2°, rearm only after leaving ≥0.35°) — not a
|
||||
time debounce, which would hide a legitimate quick re-crossing — and is measured from the
|
||||
**same hypot(stable pitch, stable roll)** that drives the bubble, so the sound can't lag the
|
||||
visual. `level.wav`'s stream id is retained and stopped before every restart / on exit.
|
||||
- The stronger **"held level" confirmation — persistent lime, the "Flat within 0.35°" label,
|
||||
and the haptic — is decided separately** by the velocity-aware `LockDetector` (≤0.2° + ≤0.3°/s
|
||||
→ 175 ms confirm). The locating *sound* never waits on it; a fast fly-through gets the bullseye
|
||||
cue but not the "you nailed it" confirmation.
|
||||
|
||||
**EDGE (1-D, future) — the "sonar staircase."** Direction + magnitude as a 4-blip pitch
|
||||
contour (rising = raise it, falling = lower it, steepness = how far; level sounds flat), one
|
||||
|
||||
Reference in New Issue
Block a user