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) <noreply@anthropic.com>
This commit is contained in:
arcattack
2026-07-16 07:47:48 -05:00
co-authored by Claude Opus 4.8
parent f4b2a7f87f
commit 90f99fe4b5
3 changed files with 62 additions and 1 deletions
+12
View File
@@ -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 &&
+10
View File
@@ -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<AudioControlEvent*> 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,
+40 -1
View File
@@ -90,6 +90,7 @@
//
#include <bt.hpp>
#include <AUDCMP.hpp> // 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);
}
}
}
}