Files
BT411/engine/MUNGA/WATCHER.h
T
arcattackandClaude Opus 4.8 1d019e8109 Audio: footstep pulse VERIFIED end-to-end to the matcher; volume chain traced (task #50)
Footstep chain progress (each step empirically verified):
- The FootStep watcher is an AudioMatchOf<Logical> (AudioLogicalTrigger extends
  AudioMatchOf, NOT AudioTriggerOf): authored config match=1 -> Start(2.0) on a
  DirectPatchSource.  [trigcfg]/[matchcfg] ctor dumps added.
- The pulse was pinned at 1: the decay lived in IntegrateMotion, which the
  current gait path NEVER CALLS.  Moved to Mech::PerformAndWatch (provably
  per-frame) with a time-based 150ms window sized to the ~10Hz watcher poll.
  The matcher now fires per stride (14/run, was 1).
- Every footstep Start is DROPPED at the renderer: the source's volumeScale is
  0 (five VolumeAudioHandler sends of exactly 0 = inert/one-shot primes).  The
  volume arrives through the authored control chain (AudioControlMultiplier
  inputs ctl 100+; one 0 input pins the product).  Chain scales identified on
  this entity: LocalVelocity (live), LocalAcceleration (live), Myomers.
  SpeedEffect (=1.0 healthy; one authored scale maps [0,1]->[1,0] INVERTED),
  UnstablePercentage (INERT PAD -- always 0!), HeatSink.CurrentTemperature.
  PRIME SUSPECT: an UnstablePercentage-fed multiplier input primes 0 once and
  never updates (the pad never changes), pinning footstep volume at 0.
- SF2 numbering fact recorded: 38 presets in AUDIO1 / 5 in AUDIO2 have
  wPreset != file index (gap at preset 77); my table keys by wPreset.  The
  184x ProgramButton01 (bank1 patch83) storm at ~7/s needs an identity check
  against index-numbering (it may be the wrong sample for that patch id).

Diagnostics added (BT_AUDIO_SPATIAL/BT_ATTRBIND_LOG): [trigcfg]/[matchcfg]
ctor dumps, [matchfire] with component ptr+class, [volset] VolumeAudioHandler,
[mult0] multiplier zero-products, scale sends with authored boundaries,
[fswatch] dedicated footstep watcher tracer (g_btFootStepAddr), SetupPatch
ENTRY with bank/patch/state, Simulation::DebugAudioWatcherCount.

NEXT: back UnstablePercentage with a real (live) member -- it is the mech's
stability 0..1 (the stabilityAlarm/gyro family) -- or confirm via a one-run
chain dump which multiplier input pins the FS source; then the footstep
transient should clear the 0.3 drop gate at walking speed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 23:52:06 -05:00

326 lines
7.9 KiB
C++

#pragma once
#include <cstdlib>
#include "cmpnnt.h"
#include "slot.h"
#include "memstrm.h"
class PlugStream;
class Simulation;
class Entity;
//##########################################################################
//########################### AttributeWatcher #######################
//##########################################################################
class AttributeWatcher:
public Component
{
public:
//
//-----------------------------------------------------------------------
// Destructor
//-----------------------------------------------------------------------
//
~AttributeWatcher();
Logical
TestInstance() const;
//
//-----------------------------------------------------------------------
// DidAttributeChange
//
// Returns True if attribute has changed since the last time
// DidAttributeChange was called.
//-----------------------------------------------------------------------
//
Logical
DidAttributeChange();
protected:
//
//-----------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------
//
AttributeWatcher(
PlugStream *stream,
Entity *entity
);
//
//-----------------------------------------------------------------------
// SendNotificationOfChange
//-----------------------------------------------------------------------
//
virtual void
SendNotificationOfChange();
//
//-----------------------------------------------------------------------
// BuildFromPage
//-----------------------------------------------------------------------
//
static void
BuildFromPage(
PlugStream *stream,
NameList *name_list,
ClassID class_ID,
ObjectID object_ID
);
//
//-----------------------------------------------------------------------
// Protected data
//-----------------------------------------------------------------------
//
Simulation*
simulation;
void*
attributePointer;
Logical
attributeChanged;
Logical
sendNotificationOnChange;
};
//##########################################################################
//########################### AttributeWatcherOf #####################
//##########################################################################
template <class T> class AttributeWatcherOf:
public AttributeWatcher
{
public:
~AttributeWatcherOf();
//
//-----------------------------------------------------------------------
// Accessors
//-----------------------------------------------------------------------
//
T
GetCurrentValue()
{return currentValue;}
T*
GetCurrentPointer()
{return &currentValue;}
//
//-----------------------------------------------------------------------
// Execute
//-----------------------------------------------------------------------
//
void
Execute();
//
//-----------------------------------------------------------------------
// PrimeWatcher
//-----------------------------------------------------------------------
//
void
PrimeWatcher();
protected:
//
//-----------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------
//
AttributeWatcherOf(
PlugStream *stream,
Entity *entity
);
//
//-----------------------------------------------------------------------
// BuildFromPage
//-----------------------------------------------------------------------
//
static void
BuildFromPage(
PlugStream *stream,
NameList *name_list,
ClassID class_ID,
ObjectID object_ID
);
private:
//
//-----------------------------------------------------------------------
// InitializeCurrentValue
//-----------------------------------------------------------------------
//
void
InitializeCurrentValue();
//
//-----------------------------------------------------------------------
// GrabCurrentValue
//-----------------------------------------------------------------------
//
void
GrabCurrentValue();
//
//-----------------------------------------------------------------------
// DumpValue
//-----------------------------------------------------------------------
//
virtual void
DumpValue(const T *attribute_ptr);
//
//-----------------------------------------------------------------------
// Private data
//-----------------------------------------------------------------------
//
T
currentValue;
#if DEBUG_LEVEL>0
Logical
dumpValue;
#endif
};
//~~~~~~~~~~~~~~~~~~~~~~~~ AttributeWatcherOf templates ~~~~~~~~~~~~~~~~~~~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
template <class T>
AttributeWatcherOf<T>::~AttributeWatcherOf()
{
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
template <class T>
AttributeWatcherOf<T>::AttributeWatcherOf(
PlugStream *stream,
Entity *entity
):
AttributeWatcher(stream, entity)
{
Check_Pointer(this);
Logical dump_value;
MemoryStream_Read(stream, &dump_value);
#if DEBUG_LEVEL>0
dumpValue = dump_value;
if (dumpValue)
{
Tell(
"AttributeWatcherOf<T>::AttributeWatcherOf - " <<
*(T*)attributePointer <<
"\n"
);
}
#endif
InitializeCurrentValue();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
template <class T> void
AttributeWatcherOf<T>::BuildFromPage(
PlugStream *stream,
NameList *name_list,
ClassID class_ID,
ObjectID object_ID
)
{
Check(stream);
Check(name_list);
AttributeWatcher::BuildFromPage(stream, name_list, class_ID, object_ID);
//
// Store dump_value
//
MEM_STRM_WRITE_ENTRY(*stream, name_list, Logical, dump_value);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
template <class T> void
AttributeWatcherOf<T>::PrimeWatcher()
{
Check(this);
#if DEBUG_LEVEL>0
if (dumpValue)
{
DumpValue((T*)attributePointer);
}
#endif
GrabCurrentValue();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
template <class T> void
AttributeWatcherOf<T>::Execute()
{
Check(this);
Check_Pointer(attributePointer);
if (getenv("BT_AUDIO_SPATIAL")) { // poll-rate probe: total watcher polls + change events
static long s_polls=0; if ((++s_polls % 2000)==0)
DEBUG_STREAM << "[watchpoll] total polls=" << s_polls << "\n" << std::flush;
extern void *g_btFootStepAddr; // DIAG: uncapped tracer on THE footstep watcher
if ((void*)attributePointer == g_btFootStepAddr) {
static long s_fsp=0;
if ((++s_fsp % 120)==0 || !(currentValue == *(T*)attributePointer))
DEBUG_STREAM << "[fswatch] poll#" << s_fsp << " cur=" << (int)*(int*)&currentValue
<< " mem=" << *(int*)attributePointer << "\n" << std::flush;
}
}
if (!(currentValue == *(T*)attributePointer))
{
if (getenv("BT_AUDIO_SPATIAL")) { static int s_chg=0; if (s_chg++<60)
DEBUG_STREAM << "[watchpoll] CHANGE attrPtr=" << (void*)attributePointer << "\n" << std::flush; }
#if DEBUG_LEVEL>0
if (dumpValue)
{
DumpValue((T*)attributePointer);
}
#endif
GrabCurrentValue();
}
Check_Fpu();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
template <class T> void
AttributeWatcherOf<T>::InitializeCurrentValue()
{
Check_Pointer(attributePointer);
currentValue = *(T*)attributePointer;
Verify(currentValue == *(T*)attributePointer);
Check_Fpu();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
template <class T> void
AttributeWatcherOf<T>::GrabCurrentValue()
{
InitializeCurrentValue();
attributeChanged = True;
if (sendNotificationOnChange)
{
SendNotificationOfChange();
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#if DEBUG_LEVEL>0
template <class T> void
AttributeWatcherOf<T>::DumpValue(const T *attribute_ptr)
#else
template <class T> void
AttributeWatcherOf<T>::DumpValue(const T*)
#endif
{
Check_Pointer(attribute_ptr);
Tell((void *)attribute_ptr << " = " << *attribute_ptr << "\n");
}