From f9546d254fa0f07afb63d989669af9b635154dcb Mon Sep 17 00:00:00 2001 From: Joe DiPrima Date: Thu, 13 Aug 2026 19:54:50 -0500 Subject: [PATCH] cadence census item 4: doppler velocity operand sample-and-held at 28Hz (gotcha 32) [T2 mechanism / T3 perceptual]. worldLinearVelocity is a raw per-render-frame position derivative; at ~59fps its gait-aliasing noise reached the doppler divisor directly. Fix is consumer-side only per the census: the relative WORLD velocity held per AudioLocation on a 28Hz wall clock; head-frame transform + geometry stay live per call; the mech4 producer untouched. BT_AUD_DOPPLER_HZ override (=0 per-call) + [doppler] receipt under BT_AUD_DOPPLER_LOG (cents/v/d/h/hm2). Measured: clock proven live (55% of calls held vs 0% in control), operand total-variation 67.8 -> 54.2 u/s-per-s (-20%) with std preserved (real stride oscillation kept). T3 flag: cents jitter only -11% on the solo rig because the loud sources ride the player's own entity at d~1 where live near-field geometry dominates -- the nearby-walking-mech warble needs an ear A/B or a 2-node rig. Bonus finding (pre-existing, receipt-exposed, NOT touched): sources linked to child entities of a moving mech read relative v = full ground speed (child worldLinearVelocity=0) -> cents ~ -200 on self-sounds; new census row. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_016bw71WVsccjwW7uKRg4aWD --- engine/MUNGA/AUDLOC.cpp | 54 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/engine/MUNGA/AUDLOC.cpp b/engine/MUNGA/AUDLOC.cpp index e028765..c69a828 100644 --- a/engine/MUNGA/AUDLOC.cpp +++ b/engine/MUNGA/AUDLOC.cpp @@ -1,3 +1,5 @@ +#include // (BT411) getenv -- the 28Hz doppler-operand clock (cadence census item 4) +#include // (BT411) per-instance sample-and-hold registry #include "munga.h" #pragma hdrstop @@ -442,6 +444,45 @@ void ); Check(&relative_velocity_temp); + // + // (BT411 cadence census item 4, gotcha 32) 28 Hz SAMPLE-AND-HOLD on the + // doppler velocity operand: worldLinearVelocity is a raw per-render-frame + // position derivative (the mech4 producer feeds other consumers and is + // NOT touched); at ~59 fps its gait-aliasing noise reaches this divisor + // directly and warbles dopplerCents per stride. The pod computed this + // once per 28 Hz frame. Hold the RELATIVE WORLD velocity per instance + // on a 28 Hz wall clock; the head-frame transform below stays live per + // call (geometry is smooth -- only the noisy operand is clocked). + // BT_AUD_DOPPLER_HZ= overrides (=0 restores per-call sampling). + // + int dopplerFresh = -1; // receipt: -1 legacy / 1 sampled / 0 held + { + static Scalar s_dopHz = -2.0f; + if (s_dopHz < -1.0f) + { + const char *hz = getenv("BT_AUD_DOPPLER_HZ"); + s_dopHz = (hz != 0 && *hz != '\0') ? (Scalar)atof(hz) : 28.0f; + } + if (s_dopHz > 0.0f) + { + struct DopplerHold { DWORD nextMs; Vector3D held; }; + static std::map s_dopHold; + DopplerHold &h = s_dopHold[this]; + const DWORD now = GetTickCount(); + if (h.nextMs == 0 || (long)(now - h.nextMs) >= 0) + { + h.held = relative_velocity_temp; + h.nextMs = now + (DWORD)(1000.0f / s_dopHz); + dopplerFresh = 1; + } + else + { + relative_velocity_temp = h.held; + dopplerFresh = 0; + } + } + } + relative_velocity.MultiplyByInverse( relative_velocity_temp, head_entity->localToWorld @@ -473,6 +514,19 @@ void { dopplerCents = cents; } + + // (BT411) doppler receipt -- jitter A/B for the 28Hz operand clock. + // hm2 = |held relative WORLD velocity|^2 (the clocked operand itself, + // geometry-free); h = 1 fresh sample / 0 held / -1 legacy per-call. + if (getenv("BT_AUD_DOPPLER_LOG")) { static int s_dl = 0; if (s_dl++ < 6000) + DEBUG_STREAM << "[doppler] t=" << (GetTickCount() % 1000000) + << " this=" << (void *)this + << " cents=" << dopplerCents + << " v=" << speed_of_source + << " d=" << distanceToSource + << " h=" << dopplerFresh + << " hm2=" << relative_velocity_temp.LengthSquared() + << "\n" << std::flush; } } else {