From 90f99fe4b577763dc8e448c6cfa88df9ef4a2b36 Mon Sep 17 00:00:00 2001 From: arcattack Date: Thu, 16 Jul 2026 07:47:48 -0500 Subject: [PATCH] Audio: FOOTSTEPS WORK -- reconstruct the lost game-side step-intensity broadcast (task #50) The final link: the footstep source's volume + brightness mix from two AudioControlMixer inputs (ctl 100 walk / 101 run) behind an entity-registered AudioControlSplitter. The object stream only ZERO-initializes those inputs (AudioControlSend one-shot initializers; mixer ctor inits 0.0) -- the runtime feed was BT game code (lost with the source), using the engine's game-facing Entity::AudioSocketIterator broadcast (the only path to those anonymous components; the splitter registers via entity->AddAudioComponent). Reconstruction [T3]: on each foot plant (the SetBodyAnimation locomotion-clip pulse), broadcast the step intensity (live ground speed on the authored 0..40 -> 0..1 velocity-curve family, floored at 0.35 to clear the 0.3 transient-drop gate) on the gait-appropriate input (run-family clips 0x0a..0x0f -> 101, walk family -> 100; the other input zeroed so gait changes don't stack). Patch-source components (VDATA 1001/1002/1005) are skipped -- AudioSource::ReceiveControl Fail()s on input-range ids. VERIFIED: FootFallInt01.wav (bank2 patch39) delivers + plays per stride; mixer sums nonzero (vol=1 at run speed); 30s drive+fire regression clean -- weapon charge/fire/sustain cycles, ready dings, buttons, engine loops, state audio (0 skips), gait advancing normally, no crashes, no stuck loops. Also this session: [seqcfg] sequence config dump (decoded the authored event streams: ctl 8=note/6=attack-vol/1=start/2=stop patterns), BT_AUDIO_NODROP diagnostic (force low-volume transient delivery at a 0.7 floor). Co-Authored-By: Claude Opus 4.8 (1M context) --- engine/MUNGA/AUDREND.cpp | 12 +++++++++++ engine/MUNGA/AUDSEQ.cpp | 10 +++++++++ game/reconstructed/mech2.cpp | 41 +++++++++++++++++++++++++++++++++++- 3 files changed, 62 insertions(+), 1 deletion(-) diff --git a/engine/MUNGA/AUDREND.cpp b/engine/MUNGA/AUDREND.cpp index b7ef6e2..c0b0597 100644 --- a/engine/MUNGA/AUDREND.cpp +++ b/engine/MUNGA/AUDREND.cpp @@ -208,6 +208,18 @@ void // Then return //-------------------------------------------------------------------------- // + // DIAG (BT_AUDIO_NODROP): deliver low-volume transient starts anyway (at a + // floor volume) to isolate volume-feed problems from the rest of the chain. + if (getenv("BT_AUDIO_NODROP") && + message->controlID == StartAudioControlID && + audio_source->GetAudioRenderType() == TransientAudioRenderType && + audio_source_volume_scale < LowAudioVolumeThreshold) + { + DEBUG_STREAM << "[spatial] NODROP forcing start src=" << (void*)audio_source + << " vol=" << audio_source_volume_scale << "\n" << std::flush; + audio_source->ReceiveControl(VolumeAudioControlID, 0.7f); + audio_source_volume_scale = 0.7f; + } if ( message->controlID == StartAudioControlID && audio_source->GetAudioRenderType() == TransientAudioRenderType && diff --git a/engine/MUNGA/AUDSEQ.cpp b/engine/MUNGA/AUDSEQ.cpp index 3d0f802..63018fb 100644 --- a/engine/MUNGA/AUDSEQ.cpp +++ b/engine/MUNGA/AUDSEQ.cpp @@ -193,6 +193,16 @@ AudioControlSequence::AudioControlSequence( audioControlEventSocket.Add(audio_control_event); } + if (getenv("BT_ATTRBIND_LOG")) { static int s_sq=0; if (s_sq++<60) { + DEBUG_STREAM << "[seqcfg] seq=" << (void*)this << " tgt=" << (void*)audio_component + << " looped=" << (int)is_looped << " div=" << (int)divisions_per_beat + << " tempo=" << (int)tempo << " events=" << (int)number_of_control_events << " "; + { SChainIteratorOf it(&audioControlEventSocket); + AudioControlEvent *e; int n=0; + while ((e = it.ReadAndNext()) != NULL && n++ < 24) + DEBUG_STREAM << *e; } + DEBUG_STREAM << "\n" << std::flush; } } + AudioControlSequenceX( audio_component, entity, diff --git a/game/reconstructed/mech2.cpp b/game/reconstructed/mech2.cpp index 82efecd..8b612c4 100644 --- a/game/reconstructed/mech2.cpp +++ b/game/reconstructed/mech2.cpp @@ -90,6 +90,7 @@ // #include +#include // AudioComponent -- the foot-plant step-intensity broadcast #pragma hdrstop #if !defined(MECH_HPP) @@ -263,7 +264,45 @@ void footStep = 1; footStepDecay = 150; // ms: ~1.5 poll periods ON, OFF well before the next stride if (getenv("BT_AUDIO_LOG")) { static int s_fs=0; if (s_fs++<40) - DEBUG_STREAM << "[audio] footstep pulse (clip " << state << ") leftover=" << footStepDecay << "\n" << std::flush; } + DEBUG_STREAM << "[audio] footstep pulse (clip " << state << ")\n" << std::flush; } + + // STEP-INTENSITY SEND [T3]: the authored footstep audio graph mixes its + // volume + brightness from two mixer inputs (ctl 100 walk / 101 run, + // reached through an entity-registered AudioControlSplitter) that the + // object stream only ZERO-initializes -- the runtime feed was BT game + // code (lost), using the engine's Entity::AudioSocketIterator broadcast + // (the only game-facing path to those anonymous components). Reconstruct + // it: on each plant, broadcast the step intensity from the live ground + // speed (the same 0..40 -> 0..1 curve family the authored velocity scales + // use), on the walking input for walk-family clips and the running input + // for run-family clips. Mixer slots are stateful; the other input is + // zeroed so gait changes don't stack. Components with matching input + // ids elsewhere (velocity-fed multiplier slots) are re-driven every frame + // by their own scales, so the broadcast cannot durably disturb them. + { + Scalar speed = localVelocity.linearMotion.Length(); + Scalar intensity = speed * (1.0f / 40.0f); + if (intensity > 1.0f) intensity = 1.0f; + if (intensity < 0.35f) intensity = 0.35f; // audible floor (> the 0.3 transient drop gate) + int runFamily = (state >= 0x0a && state <= 0x0f); // wrr/wrl/rrr/rrl/rwr/rwl + Entity::AudioSocketIterator it(this); + Component *c; + while ((c = it.ReadAndNext()) != NULL) + { + // Only the control-graph components (mixers/multipliers/splitters) + // accept the 100+ input-range ids; an AudioSource's ReceiveControl + // Fail()s on unknown ids, so skip the patch-source classes + // (Direct/Dynamic3D/Static3D -- VDATA L4 ids 1001/1002/1005). + { + int cid = (int)c->GetClassID(); + if (cid == 1001 || cid == 1002 || cid == 1005) + continue; + } + AudioComponent *ac = (AudioComponent *)c; + ac->ReceiveControl((AudioControlID)(runFamily ? 101 : 100), intensity); + ac->ReceiveControl((AudioControlID)(runFamily ? 100 : 101), 0.0f); + } + } } }