Commit Graph

3 Commits

Author SHA1 Message Date
thejayman77 0cb315fdcc 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>
2026-07-17 11:18:04 -04:00
thejayman77 3ebb36f265 Address Codex audit of ticking (36c0cfa): dynamic + init-race fixes
P1 - Tick cadence now reacts immediately to changing tilt. The due time is
recomputed every update from the last-tick anchor plus the CURRENT interval,
so dropping from 5deg to 0.4deg accelerates to the fast cadence at once
instead of waiting out the stale slow interval. Anchor advances by whole
intervals (no drift) and resyncs to now if a full interval behind (no burst).
Added far->near and near->far transition tests.

P1 - One-shot cues no longer lost to async loading. SonarSoundPool queues a
Level asked for before its sample loads and plays it on load-complete (and
cancels the queued Level on unlock/gate). VoiceSpeaker queues the latest
phrase requested before TTS finishes init and speaks it on ready. The policy
fires these once, so they can't rely on retries.

P2 - Unlock stops the level.wav tail BEFORE playing the resumed tick (order
was reversed, allowing a brief overlap).

P2 - Audio-mode preference write moved from the screen's rememberCoroutineScope
to container.applicationScope, so a quick navigation can't cancel it (matches
the earlier persistence hardening).

Cleanup: AudioAssistMode and LevelPipeline comments now describe ticking, not
the retired radar pings/homing bands; AUDIO_DESIGN_LOG cadence principle
reconciled with the 125 ms continuous-tick near rate.

78 tests passing; assembleDebug clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 10:01:52 -04:00
thejayman77 36c0cfa5ab Audio: Surface proximity ticking + Voice settled-change; stage Edge staircase
Replaces the hands-free audio assist after extensive on-device iteration
(full journey + rejected approaches in AUDIO_DESIGN_LOG.md; current design
in SOUND_DESIGN_HANDOFF.md, from Jay's sound-design sessions).

Surface (TONES) = continuous proximity ticking: tick.wav repeats faster as
you near level (axis-agnostic warmer/colder, the rate is the message).
- Exponential rate in stable tilt error, clamped 1.5/s (>=~5 deg) to 8/s
  (<=~0.4 deg), hard-capped; constant loudness (acceleration is the signal).
- Deadline scheduler off a monotonic clock (SystemClock.elapsedRealtime):
  advances a next-tick deadline, so no drift and no catch-up bursts; <=1 tick
  per sensor update; immediate tick on re-entry (enable/return FACE_UP/unlock).
- Lock reuses LockDetector.isLocked (dwell + hysteresis, no second audio
  threshold): stop ticking, play level.wav once, silence while held. The
  1.39s level stream is retained and stopped on unlock/disable so a fresh
  tick can't overlap its tail.
- Pure SurfaceTickPolicy (unit-tested: anchors/clamps, monotonic rates,
  gating, immediate re-entry, no catch-up burst, level-once, resume on unlock).

Voice (VOICE) = settled-change announcer: one correction on the dominant
axis, silent while moving, speaks again on settle only if direction changed,
crossed coarse->fine, or reached lock. No periodic repeat. (VoiceGuidancePolicy,
unit-tested.)

Assets: tick.wav + level.wav downsampled to mono 44.1kHz in res/raw (from
Jay's SoundQ-derived 96k masters). AudioAssistMode adds OFF/TONES/VOICE.

Retired the settle-gated two-ding packet scheduler (AudioAssistStateMachine +
its ding assets) - superseded by ticking for Surface; logged as tested/rejected.

Edge staircase (1-D, future): 5 pitch-contour masters staged in
sonar-staircase-v3/ for when Edge mode is built.

76 tests passing; assembleDebug clean; blessed on-device.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 09:43:18 -04:00