Files
BT411/engine/MUNGA/AUDREND.cpp
T
arcattackandClaude Opus 4.8 90f99fe4b5 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>
2026-07-16 07:47:48 -05:00

543 lines
15 KiB
C++

#include "munga.h"
#pragma hdrstop
#include "audrend.h"
#include "audent.h"
#include "jmover.h"
#include "app.h"
//#############################################################################
//########################### AudioRenderer #############################
//#############################################################################
//
//#############################################################################
// AudioRenderer
//#############################################################################
//
AudioRenderer::AudioRenderer(RendererRate render_rate):
Renderer(
render_rate,
MaxRendererComplexity,
DefaultRendererPriority,
AudioInterestType,
DefaultInterestDepth,
AudioRendererClassID
),
audioEventSocket(NULL, False)
{
audioHead = NULL;
#ifdef LAB_ONLY
sourceClippedCount = 0;
sourceStartCount = 0;
sourceSuspendCount = 0;
sourceResumeCount = 0;
doubleTriggerCount = 0;
resourceStealCount = 0;
resourceStealPriorityCount = 0;
resourceStealVolumeCount = 0;
stealShortDurationCount = 0;
#endif
}
//
//#############################################################################
// ~AudioRenderer
//#############################################################################
//
AudioRenderer::~AudioRenderer()
{
Unregister_Object(audioHead);
delete audioHead;
#ifdef LAB_ONLY
cout << "AudioRenderer::~AudioRenderer - sourceClippedCount="
<< sourceClippedCount << "\n";
cout << "AudioRenderer::~AudioRenderer - sourceStartCount="
<< sourceStartCount << "\n";
cout << "AudioRenderer::~AudioRenderer - sourceSuspendCount="
<< sourceSuspendCount << "\n";
cout << "AudioRenderer::~AudioRenderer - sourceResumeCount="
<< sourceResumeCount << "\n";
cout << "AudioRenderer::~AudioRenderer - doubleTriggerCount="
<< doubleTriggerCount << "\n";
cout << "AudioRenderer::~AudioRenderer - resourceStealCount="
<< resourceStealCount << "\n";
cout << "AudioRenderer::~AudioRenderer - resourceStealPriorityCount="
<< resourceStealPriorityCount << "\n";
cout << "AudioRenderer::~AudioRenderer - resourceStealVolumeCount="
<< resourceStealVolumeCount << "\n";
cout << "AudioRenderer::~AudioRenderer - stealShortDurationCount="
<< stealShortDurationCount << "\n";
#endif
}
//
//#############################################################################
// Initialize
//#############################################################################
//
void
AudioRenderer::Initialize()
{
audioHead = MakeAudioHead();
Register_Object(audioHead);
}
//
//#############################################################################
// TestInstance
//#############################################################################
//
Logical
AudioRenderer::TestInstance() const
{
Renderer::TestInstance();
if (audioHead != NULL)
{
Check(audioHead);
}
Check(&audioEventSocket);
return True;
}
//
//#############################################################################
// LinkToEntity
//#############################################################################
//
void
AudioRenderer::LinkToEntity(Entity *entity)
{
Check(this);
Check(entity);
//
//--------------------------------------------------------------------------
// Link the head to the entity
//--------------------------------------------------------------------------
//
Check(audioHead);
audioHead->LinkToEntity(entity);
//
//--------------------------------------------------------------------------
// Call inherited method
//--------------------------------------------------------------------------
//
Renderer::LinkToEntity(entity);
}
//
//#############################################################################
// ExecuteImplementation
//#############################################################################
//
void
AudioRenderer::ExecuteImplementation(
RendererComplexity,
RendererOrigin::InterestingEntityIterator*
)
{
Check(this);
Check(audioHead);
audioHead->Execute();
}
//
//#############################################################################
// PostAudioRequestMessage
//#############################################################################
//
void
AudioRenderer::PostAudioRequestMessage(
AudioSource *audio_source,
AudioSource::RequestMessage *message
)
{
Check(this);
Check(audio_source);
Check(message);
//
//--------------------------------------------------------------------------
// If audio source is clipped and the source is transient
// Then ignore
// Else set priority and volume
//--------------------------------------------------------------------------
//
AudioSourcePriority
audio_source_priority;
AudioControlValue
audio_source_volume_scale;
if (audio_source->IsAudioSourceClipped(GetAudioHead()))
{
if (getenv("BT_AUDIO_SPATIAL")) { static int s_cl=0; if (s_cl++<40)
DEBUG_STREAM << "[spatial] CLIPPED src=" << (void*)audio_source
<< " headPos=(" << GetAudioHead()->GetHeadEntity()->localOrigin.linearPosition.x
<< "," << GetAudioHead()->GetHeadEntity()->localOrigin.linearPosition.z << ")\n" << std::flush; }
//
// If it is a transient source then ignore request
//
if (audio_source->GetAudioRenderType() == TransientAudioRenderType)
{
#ifdef LAB_ONLY
sourceClippedCount++;
#endif
return;
}
audio_source_priority = audio_source->GetAudioSourcePriority();
audio_source_volume_scale = 0.0f;
}
else
{
audio_source_priority = audio_source->GetAudioSourcePriority();
audio_source_volume_scale = audio_source->CalculateSourceVolumeScale();
if (getenv("BT_AUDIO_SPATIAL")) { static int s_vs=0; if ((s_vs++ % 120)==0)
DEBUG_STREAM << "[spatial] request src=" << (void*)audio_source
<< " vol=" << audio_source_volume_scale
<< " headPos=(" << GetAudioHead()->GetHeadEntity()->localOrigin.linearPosition.x
<< "," << GetAudioHead()->GetHeadEntity()->localOrigin.linearPosition.z << ")\n" << std::flush; }
}
//
//--------------------------------------------------------------------------
// If this a transient source and volume is lower than threshold
// 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 &&
audio_source_volume_scale < LowAudioVolumeThreshold
)
{
if (getenv("BT_AUDIO_SPATIAL")) { static int s_dr=0; if (s_dr++<40)
DEBUG_STREAM << "[spatial] DROP transient start src=" << (void*)audio_source
<< " vol=" << audio_source_volume_scale
<< " (below threshold " << LowAudioVolumeThreshold << ")\n" << std::flush; }
#ifdef LAB_ONLY
sourceClippedCount++;
#endif
return;
}
if (getenv("BT_AUDIO_SPATIAL") && message->controlID == StartAudioControlID) {
static int s_st=0; if (s_st++<40)
DEBUG_STREAM << "[spatial] START request src=" << (void*)audio_source
<< " vol=" << audio_source_volume_scale << "\n" << std::flush; }
//
//--------------------------------------------------------------------------
// Create audio weight based on priority and volume
// Add audio event
//--------------------------------------------------------------------------
//
AudioWeighting
audio_weight(audio_source_priority, audio_source_volume_scale);
AudioEvent
*audio_event;
audio_event = new AudioEvent(audio_source, message);
Register_Object(audio_event);
audioEventSocket.AddValue(audio_event, audio_weight);
}
//
//#############################################################################
// ProcessAudioRequestMessage
//#############################################################################
//
Logical
AudioRenderer::ProcessAudioRequestMessage()
{
//
// Process high priority, high volume event
//
AudioEventIterator
iterator(&audioEventSocket);
AudioEvent
*audio_event;
Logical stuff = False;
while ((audio_event = iterator.GetCurrent()) != NULL)
{
stuff = True;
Check(audio_event);
audio_event->Process();
//return True;
}
return stuff;
}
//
//#############################################################################
// FlushAudioMessages
//#############################################################################
//
void
AudioRenderer::FlushAudioMessages(AudioSource *audio_source)
{
Check(this);
Check(audio_source);
//
// Process matching events
//
AudioEventIterator
iterator(&audioEventSocket);
AudioEvent
*audio_event;
while ((audio_event = iterator.ReadAndNext()) != NULL)
{
Check(audio_event);
if (audio_event->targetReceiver.GetCurrent() == audio_source)
{
if (audio_event->messageToSend->controlID == StopAudioControlID)
{
audio_event->Process();
}
else
{
Unregister_Object(audio_event);
delete audio_event;
}
}
}
}
//
//#############################################################################
// MakeAudioHead
//#############################################################################
//
AudioHead*
AudioRenderer::MakeAudioHead()
{
return new AudioHead;
}
//
//#############################################################################
// StartEntityEffectImplementation
//#############################################################################
//
void
AudioRenderer::StartEntityEffectImplementation(
Entity *parent_entity,
DamageZone *damage_zone,
ResourceDescription::ResourceID resource_ID
)
{
SET_AUDIO_RENDERER();
Check(this);
Check(parent_entity);
Check(damage_zone);
Verify(resource_ID != ResourceDescription::NullResourceID);
//
//--------------------------------------------------------------------------
// Get the audio resource for this effect
//--------------------------------------------------------------------------
//
ResourceDescription *audio_resource_description;
Check(application);
Check(application->GetResourceFile());
audio_resource_description =
application->GetResourceFile()->SearchList(
resource_ID,
ResourceDescription::AudioStreamListResourceType
);
if (audio_resource_description == NULL)
{
CLEAR_AUDIO_RENDERER();
return;
}
Check(audio_resource_description);
//
//--------------------------------------------------------------------------
// Get the resource ID for the model resource for the audio effect entity
//--------------------------------------------------------------------------
//
ResourceDescription *model_resource_description;
Check(application);
Check(application->GetResourceFile());
Check(audio_resource_description);
model_resource_description =
application->GetResourceFile()->SearchList(
audio_resource_description->resourceID,
ResourceDescription::ModelListResourceType
);
if (model_resource_description == NULL)
{
CLEAR_AUDIO_RENDERER();
return;
}
Check(model_resource_description);
model_resource_description->Lock();
audio_resource_description->Lock();
//
//--------------------------------------------------------------------------
// Get the entity segment
//--------------------------------------------------------------------------
//
Entity *linked_entity;
AudioRepresentation audio_representation;
EntitySegment *parent_entity_segment;
linked_entity = GetLinkedEntity();
Check(linked_entity);
audio_representation =
(AudioRepresentation)parent_entity->GetAudioRepresentation(
linked_entity
);
parent_entity_segment = damage_zone->GetCurrentEffectSite(
(audio_representation == InternalAudioRepresentation) ?
DamageZone::InternalAudioEffectSite :
DamageZone::ExternalAudioEffectSite
);
Check(parent_entity_segment);
//
//--------------------------------------------------------------------------
// Create the audio effect entity with this resource
//--------------------------------------------------------------------------
//
JointedMover *jointed_mover;
Verify(parent_entity->IsDerivedFrom(JointedMover::GetClassDerivations()));
jointed_mover = Cast_Object(JointedMover*, parent_entity);
Check(jointed_mover);
AudioEntity::MakeMessage
make_message(
model_resource_description->resourceID,
jointed_mover,
parent_entity_segment
);
#if DEBUG_LEVEL>0
AudioEntity *audio_entity =
#endif
AudioEntity::Make(&make_message);
Register_Object(audio_entity);
model_resource_description->Unlock();
audio_resource_description->Unlock();
CLEAR_AUDIO_RENDERER();
}
//~~~~~~~~~~~~~~~~~~~~~~~~ AudioRenderer profile bits ~~~~~~~~~~~~~~~~~~~~~~~~~
#if defined(TRACE_AUDIO_RENDERER)
BitTrace Audio_Renderer("Audio Renderer");
#endif
#if defined(TRACE_AUDIO_RENDERER_CREATE_OBJECTS)
BitTrace Audio_Renderer_Create_Objects("Audio Renderer Create Objects");
#endif
#if defined(TRACE_AUDIO_RENDERER_DESTROY_OBJECTS)
BitTrace Audio_Renderer_Destroy_Objects("Audio Renderer Destroy Objects");
#endif
#if defined(TRACE_AUDIO_RENDERER_START_HANDLER)
BitTrace Audio_Renderer_Start_Handler("Audio Renderer Start Handler");
#endif
#if defined(TRACE_AUDIO_RENDERER_STOP_HANDLER)
BitTrace Audio_Renderer_Stop_Handler("Audio Renderer Stop Handler");
#endif
#if defined(TRACE_AUDIO_RENDERER_EXECUTE)
BitTrace Audio_Renderer_Execute("Audio Renderer Execute");
#endif
//#############################################################################
//############################## AudioEvent #############################
//#############################################################################
MemoryBlock *AudioEvent::GetAllocatedMemory()
{
static MemoryBlock allocatedMemory(sizeof(Event), AUDIOEVENT_MEMORYBLOCK_ALLOCATION, AUDIOEVENT_MEMORYBLOCK_ALLOCATION, "AudioEvents");
return &allocatedMemory;
}
//
//#############################################################################
//#############################################################################
//
AudioEvent::AudioEvent(
AudioSource *target,
AudioSource::RequestMessage *message
):
targetReceiver(this)
{
Check(target);
Check(message);
//
// Store the message
//
size_t long_size = (message->messageLength+3)>>2;
messageToSend = (AudioSource::RequestMessage*)new long[long_size];
Check_Pointer(messageToSend);
Register_Pointer(messageToSend);
Mem_Copy(
messageToSend,
message,
message->messageLength,
long_size*sizeof(long)
);
//
// Store the target receiver
//
targetReceiver.Add(target);
}
//
//#############################################################################
//#############################################################################
//
AudioEvent::~AudioEvent()
{
Unregister_Pointer(messageToSend);
delete messageToSend;
}
//
//#############################################################################
//#############################################################################
//
void
AudioEvent::ReleaseLinkHandler(
Socket*,
Plug*
)
{
Unregister_Object(this);
delete this;
}