Files
BT411/engine/MUNGA/AUDWTHR.cpp
T
Joe DiPrimaandClaude Opus 5 e058b6acb7 #99: trace the coolant-leak alarm end to end -- it IS implemented; found the real defect
Oracle: "alarm is not sounding despite an active leak" ... "was sounding,
resolved a leak, but there was another".

RETRACTION FIRST.  I previously told the user the coolant alarm was "genuinely
unbuilt -- there is no alarm implementation to starve".  That was wrong, and it
was asserted without checking.  The alarm is fully implemented and the entire
chain works.  Traced live, in order:

  Condenser6.ReportLeak (= HeatSink::coolantActive) changes 0 -> 1
    -> [watchpoll] CHANGE on that exact address
    -> [matchfire] val=1 -> ctl 1  (StartAudioControlID)
    -> [seqstart] the alarm AudioControlSequence, events=25 looped=1
    -> [seqsend] ctl 8/16 (select), 6/1 (volume), 1/0 (START), 2/0 (STOP), ...
       a three-part looping alarm: two chirps then an 8s sustained tone.

The authored design, read from BTL4.RES: 19 subsystems per mech each bind TWO
AudioLogicalTriggers to their ReportLeak flag -- match 1 -> Start, match 0 ->
Stop -- and ALL of them drive ONE shared alarm sequence.

SO WHY WAS IT SILENT IN .674?  Almost certainly the audio source pool, fixed
after that build in ad9dfad.  Every player log from .674 is saturated with
ACQUIRE FAILED (3k-6.5k lines each, starting ~10% in and never recovering); an
alarm that cannot acquire an OpenAL source is silent.  The bench here shows 0
acquire failures on the current build.  ⚠ NOT PROVEN: this bench has no audio
device (pool census reads live=0 pooled=0), so the control chain is verified but
final playback is not.  Field confirmation needed.

THE SECOND SYMPTOM IS A REAL, REPRODUCED DEFECT AND IT IS IN THE AUTHORED DATA.
All 19 subsystems -- Condenser1-6, GeneratorA-D, Myomers, PPC_1/2, ERMLaser_1-3,
SRM6_1/2, Avionics -- share the single alarm sequence, and EACH one's leak-clear
sends an UNCONDITIONAL Stop.  So the moment any one of them stops leaking the
alarm goes silent, even while others are still leaking.  That is exactly what was
reported.  Since both the authoring and the MUNGA watcher code are authentic,
this is 1995 behaviour, and changing it is a deliberate divergence -- flagged for
a fidelity call rather than "fixed" unilaterally.

Diagnostics added (all under the existing BT_ATTRBIND_LOG gate):
  [seqcfg]  extended: the sequence's authored event list
  [seqstart] extended: events / looped / tempo / divisionsPerBeat
  [seqrun]  RunSequence entry state (isRunning / iterator / current event)
  [seqwait] an event exists but is not yet ready, with the time delta
  [seqsend] the sequence actually emitting a control
  [idlewatch] which components receive the idle tick that advances sequences
  [hsparm] extended with &ReportLeak so audio bindings can be correlated
plus scratchpad/night8/leakaudio.sh.

NOTE ON THE PROBES, because it cost real time twice today: [seqrun] first used a
single shared static counter, which the busy sequences consumed so the alarm
sequence never printed -- reading as "RunSequence is never called".  It is now
throttled PER SEQUENCE (runProbeCount).  A capped diagnostic that is silent is
not evidence of absence; the same trap produced a false "seqsend x0" and a false
"the trigger never fires" earlier in this investigation.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 18:07:40 -05:00

1105 lines
30 KiB
C++

#include <cstdlib>
#include "munga.h"
#pragma hdrstop
#include "audwthr.h"
#include "objstrm.h"
#include "namelist.h"
//#############################################################################
//########################### AudioIdleWatcher ##########################
//#############################################################################
//
//#############################################################################
//#############################################################################
//
AudioIdleWatcher::AudioIdleWatcher(
PlugStream *stream,
Entity *entity
):
Component(stream),
audioComponentSocket(this)
{
AudioComponent *audio_component;
PlugStream_ReadObjectIDAndFindPlug(stream, &audio_component);
AudioIdleWatcherX(audio_component, entity);
}
//
//#############################################################################
//#############################################################################
//
void
AudioIdleWatcher::AudioIdleWatcherX(
AudioComponent *audio_component,
Entity *entity
)
{
Check(audio_component);
Check(entity);
audioComponentSocket.Add(audio_component);
entity->AddAudioWatcher(this);
}
//
//#############################################################################
//#############################################################################
//
AudioIdleWatcher::~AudioIdleWatcher()
{
}
//
//#############################################################################
//#############################################################################
//
void
AudioIdleWatcher::BuildFromPage(
PlugStream *stream,
NameList *name_list,
ClassID class_ID,
ObjectID object_ID
)
{
Component::BuildFromPage(stream, name_list, class_ID, object_ID);
CString audio_component_name("audio_component");
PlugStream_FindEntryAndWriteObjectID(stream, name_list, audio_component_name);
}
//
//#############################################################################
//#############################################################################
//
Logical
AudioIdleWatcher::TestInstance() const
{
Component::TestInstance();
Check(&audioComponentSocket);
return True;
}
//
//#############################################################################
//#############################################################################
//
void
AudioIdleWatcher::ReleaseLinkHandler(
Socket *socket,
Plug *plug
)
{
if (socket == &audioComponentSocket)
{
Check(&audioComponentSocket);
Check(socket);
Unregister_Object(this);
delete this;
}
else
{
Component::ReleaseLinkHandler(socket, plug);
}
}
//
//#############################################################################
//#############################################################################
//
void
AudioIdleWatcher::Execute()
{
Check(this);
Check(audioComponentSocket.GetCurrent());
if (getenv("BT_ATTRBIND_LOG")) { static int s_iw=0; if ((s_iw++ % 400)==0)
DEBUG_STREAM << "[idlewatch] #" << s_iw << " comp="
<< (void *)audioComponentSocket.GetCurrent() << std::endl; }
audioComponentSocket.GetCurrent()->ReceiveControl(
IdleAudioControlID,
0.0f
);
}
//#############################################################################
//############################## AudioMotionTrigger #####################
//#############################################################################
//
//#############################################################################
//#############################################################################
//
AudioMotionTrigger::AudioMotionTrigger(
PlugStream *stream,
Entity *entity
):
AudioTriggerOf<Motion>(stream, entity)
{
MemoryStream_Read(stream, &motionType);
MemoryStream_Read(stream, &motionValue);
if (getenv("BT_ATTRBIND_LOG")) { static int s_mt=0; if (s_mt++<40)
DEBUG_STREAM << "[motiontrigcfg] attrPtr=" << (void*)attributePointer
<< " motionType=" << (int)motionType << " (0=linear,1=angular)"
<< " motionValue=" << (int)motionValue << " (0=X,1=Y,2=Z,3=len)"
<< "\n" << std::flush; }
PrimeWatcher();
}
//
//#############################################################################
//#############################################################################
//
AudioMotionTrigger::~AudioMotionTrigger()
{
}
//
//#############################################################################
//#############################################################################
//
void
AudioMotionTrigger::BuildFromPage(
PlugStream *stream,
NameList *name_list,
ClassID class_ID,
ObjectID object_ID
)
{
AudioTriggerOf<Motion>::BuildFromPage(stream, name_list, class_ID, object_ID);
//
// Store fields
//
MEM_STRM_WRITE_ENTRY(*stream, name_list, Enumeration, motion_type);
MEM_STRM_WRITE_ENTRY(*stream, name_list, Enumeration, motion_value);
}
//
//#############################################################################
//#############################################################################
//
AudioControlValue
AudioMotionTrigger::ExtractInterestingValue(const Motion *attribute_ptr)
{
Check(this);
Check(attribute_ptr);
switch (motionType)
{
case LinearMotionType:
switch (motionValue)
{
case XMotionValue:
return attribute_ptr->linearMotion.x;
case YMotionValue:
return attribute_ptr->linearMotion.y;
case ZMotionValue:
return attribute_ptr->linearMotion.z;
case LengthMotionValue:
return attribute_ptr->linearMotion.Length();
}
break;
case AngularMotionType:
switch (motionValue)
{
case XMotionValue:
return attribute_ptr->angularMotion.x;
case YMotionValue:
return attribute_ptr->angularMotion.y;
case ZMotionValue:
return attribute_ptr->angularMotion.z;
case LengthMotionValue:
return attribute_ptr->angularMotion.Length();
}
break;
}
Fail("AudioMotionTrigger::ExtractInterestingValue - Should never reach here");
return 0.0f;
}
//
//#############################################################################
//#############################################################################
//
#if DEBUG_LEVEL>0
void
AudioMotionTrigger::DumpValue(const Motion *attribute_ptr)
#else
void
AudioMotionTrigger::DumpValue(const Motion*)
#endif
{
Check(this);
Check(attribute_ptr);
switch (motionType)
{
case LinearMotionType:
switch (motionValue)
{
case XMotionValue:
Dump(attribute_ptr->linearMotion.x);
break;
case YMotionValue:
Dump(attribute_ptr->linearMotion.y);
break;
case ZMotionValue:
Dump(attribute_ptr->linearMotion.z);
break;
case LengthMotionValue:
Dump(attribute_ptr->linearMotion.Length());
break;
}
break;
case AngularMotionType:
switch (motionValue)
{
case XMotionValue:
Dump(attribute_ptr->angularMotion.x);
break;
case YMotionValue:
Dump(attribute_ptr->angularMotion.y);
break;
case ZMotionValue:
Dump(attribute_ptr->angularMotion.z);
break;
case LengthMotionValue:
Dump(attribute_ptr->angularMotion.Length());
break;
}
break;
}
}
//#############################################################################
//############################## AudioMotionScale #######################
//#############################################################################
//
//#############################################################################
//#############################################################################
//
AudioMotionScale::AudioMotionScale(
PlugStream *stream,
Entity *entity
):
AudioScaleOf<Motion>(stream, entity)
{
MemoryStream_Read(stream, &motionType);
MemoryStream_Read(stream, &motionValue);
if (getenv("BT_ATTRBIND_LOG")) { static int s_ms=0; if (s_ms++<40)
DEBUG_STREAM << "[motionscalecfg] attrPtr=" << (void*)attributePointer
<< " motionType=" << (int)motionType << " (0=linear,1=angular)"
<< " motionValue=" << (int)motionValue << " (0=X,1=Y,2=Z,3=len)"
<< "\n" << std::flush; }
PrimeWatcher();
}
//
//#############################################################################
//#############################################################################
//
AudioMotionScale::~AudioMotionScale()
{
}
//
//#############################################################################
//#############################################################################
//
void
AudioMotionScale::BuildFromPage(
PlugStream *stream,
NameList *name_list,
ClassID class_ID,
ObjectID object_ID
)
{
AudioScaleOf<Motion>::BuildFromPage(stream, name_list, class_ID, object_ID);
//
// Store fields
//
MEM_STRM_WRITE_ENTRY(*stream, name_list, Enumeration, motion_type);
MEM_STRM_WRITE_ENTRY(*stream, name_list, Enumeration, motion_value);
}
//
//#############################################################################
//#############################################################################
//
AudioControlValue
AudioMotionScale::ExtractInterestingValue(const Motion *attribute_ptr)
{
Check(this);
Check(attribute_ptr);
switch (motionType)
{
case LinearMotionType:
switch (motionValue)
{
case XMotionValue:
return attribute_ptr->linearMotion.x;
case YMotionValue:
return attribute_ptr->linearMotion.y;
case ZMotionValue:
return attribute_ptr->linearMotion.z;
case LengthMotionValue:
return attribute_ptr->linearMotion.Length();
}
break;
case AngularMotionType:
switch (motionValue)
{
case XMotionValue:
return attribute_ptr->angularMotion.x;
case YMotionValue:
return attribute_ptr->angularMotion.y;
case ZMotionValue:
return attribute_ptr->angularMotion.z;
case LengthMotionValue:
return attribute_ptr->angularMotion.Length();
}
break;
}
Fail("AudioMotionScale::ExtractInterestingValue - Should never reach here");
return 0.0f;
}
//
//#############################################################################
//#############################################################################
//
#if DEBUG_LEVEL>0
void
AudioMotionScale::DumpValue(const Motion *attribute_ptr)
#else
void
AudioMotionScale::DumpValue(const Motion*)
#endif
{
Check(this);
Check(attribute_ptr);
switch (motionType)
{
case LinearMotionType:
switch (motionValue)
{
case XMotionValue:
Dump(attribute_ptr->linearMotion.x);
break;
case YMotionValue:
Dump(attribute_ptr->linearMotion.y);
break;
case ZMotionValue:
Dump(attribute_ptr->linearMotion.z);
break;
case LengthMotionValue:
Dump(attribute_ptr->linearMotion.Length());
break;
}
break;
case AngularMotionType:
switch (motionValue)
{
case XMotionValue:
Dump(attribute_ptr->angularMotion.x);
break;
case YMotionValue:
Dump(attribute_ptr->angularMotion.y);
break;
case ZMotionValue:
Dump(attribute_ptr->angularMotion.z);
break;
case LengthMotionValue:
Dump(attribute_ptr->angularMotion.Length());
break;
}
break;
}
}
//#############################################################################
//############################## AudioHingeScale ########################
//#############################################################################
//
//#############################################################################
//#############################################################################
//
AudioHingeScale::AudioHingeScale(
PlugStream *stream,
Entity *entity
):
AudioScaleOf<Hinge>(stream, entity)
{
PrimeWatcher();
}
//
//#############################################################################
//#############################################################################
//
AudioHingeScale::~AudioHingeScale()
{
}
//
//#############################################################################
//#############################################################################
//
AudioControlValue
AudioHingeScale::ExtractInterestingValue(const Hinge *attribute_ptr)
{
Check(this);
Check(attribute_ptr);
return attribute_ptr->rotationAmount;
}
//
//#############################################################################
//#############################################################################
//
#if DEBUG_LEVEL>0
void
AudioHingeScale::DumpValue(const Hinge *attribute_ptr)
#else
void
AudioHingeScale::DumpValue(const Hinge*)
#endif
{
Check(this);
Check(attribute_ptr);
Dump(attribute_ptr->rotationAmount);
}
//#############################################################################
//############################## AudioScalarTrigger #####################
//#############################################################################
//
//#############################################################################
//#############################################################################
//
AudioScalarTrigger::AudioScalarTrigger(
PlugStream *stream,
Entity *entity
):
AudioTriggerOf<Scalar>(stream, entity)
{
PrimeWatcher();
}
//
//#############################################################################
//#############################################################################
//
AudioScalarTrigger::~AudioScalarTrigger()
{
}
//
//#############################################################################
//#############################################################################
//
void
AudioScalarTrigger::BuildFromPage(
PlugStream *stream,
NameList *name_list,
ClassID class_ID,
ObjectID object_ID
)
{
AudioTriggerOf<Scalar>::BuildFromPage(stream, name_list, class_ID, object_ID);
}
//
//#############################################################################
//#############################################################################
//
AudioControlValue
AudioScalarTrigger::ExtractInterestingValue(const Scalar *attribute_ptr)
{
Check(this);
Check_Pointer(attribute_ptr);
return *attribute_ptr;
}
//#############################################################################
//####################### AudioScalarDeltaTrigger #######################
//#############################################################################
//
//#############################################################################
//#############################################################################
//
AudioScalarDeltaTrigger::AudioScalarDeltaTrigger(
PlugStream *stream,
Entity *entity
):
AudioDeltaOf<Scalar>(stream, entity)
{
PrimeWatcher();
}
//
//#############################################################################
//#############################################################################
//
AudioScalarDeltaTrigger::~AudioScalarDeltaTrigger()
{
}
//
//#############################################################################
//#############################################################################
//
void
AudioScalarDeltaTrigger::BuildFromPage(
PlugStream *stream,
NameList *name_list,
ClassID class_ID,
ObjectID object_ID
)
{
AudioDeltaOf<Scalar>::BuildFromPage(stream, name_list, class_ID, object_ID);
}
//#############################################################################
//############################## AudioScalarScale #######################
//#############################################################################
//
//#############################################################################
//#############################################################################
//
AudioScalarScale::AudioScalarScale(
PlugStream *stream,
Entity *entity
):
AudioScaleOf<Scalar>(stream, entity)
{
PrimeWatcher();
}
//
//#############################################################################
//#############################################################################
//
AudioScalarScale::~AudioScalarScale()
{
}
//
//#############################################################################
//#############################################################################
//
AudioControlValue
AudioScalarScale::ExtractInterestingValue(const Scalar *attribute_ptr)
{
Check(this);
Check_Pointer(attribute_ptr);
return *attribute_ptr;
}
//#############################################################################
//######################### AudioLogicalTrigger #########################
//#############################################################################
//
//#############################################################################
//#############################################################################
//
AudioLogicalTrigger::AudioLogicalTrigger(
PlugStream *stream,
Entity *entity
):
AudioMatchOf<Logical>(stream, entity)
{
PrimeWatcher();
}
//
//#############################################################################
//#############################################################################
//
AudioLogicalTrigger::~AudioLogicalTrigger()
{
}
//
//#############################################################################
//#############################################################################
//
void
AudioLogicalTrigger::BuildFromPage(
PlugStream *stream,
NameList *name_list,
ClassID class_ID,
ObjectID object_ID
)
{
AudioMatchOf<Logical>::BuildFromPage(stream, name_list, class_ID, object_ID);
}
//
//#############################################################################
//#############################################################################
//
int
AudioLogicalTrigger::ExtractInterestingValue(const Logical *attribute_ptr)
{
Check(this);
Check_Pointer(attribute_ptr);
return *attribute_ptr;
}
//#############################################################################
//######################### AudioEnumerationTrigger #####################
//#############################################################################
//
//#############################################################################
//#############################################################################
//
AudioEnumerationTrigger::AudioEnumerationTrigger(
PlugStream *stream,
Entity *entity
):
AudioMatchOf<Enumeration>(stream, entity)
{
PrimeWatcher();
}
//
//#############################################################################
//#############################################################################
//
AudioEnumerationTrigger::~AudioEnumerationTrigger()
{
}
//
//#############################################################################
//#############################################################################
//
void
AudioEnumerationTrigger::BuildFromPage(
PlugStream *stream,
NameList *name_list,
ClassID class_ID,
ObjectID object_ID
)
{
AudioMatchOf<Enumeration>::BuildFromPage(stream, name_list, class_ID, object_ID);
}
//
//#############################################################################
//#############################################################################
//
int
AudioEnumerationTrigger::ExtractInterestingValue(const Enumeration *attribute_ptr)
{
Check(this);
Check_Pointer(attribute_ptr);
return *attribute_ptr;
}
//#############################################################################
//###################### AudioEnumerationDeltaTrigger ###################
//#############################################################################
//
//#############################################################################
//#############################################################################
//
AudioEnumerationDeltaTrigger::AudioEnumerationDeltaTrigger(
PlugStream *stream,
Entity *entity
):
AudioDeltaOf<Enumeration>(stream, entity)
{
PrimeWatcher();
}
//
//#############################################################################
//#############################################################################
//
AudioEnumerationDeltaTrigger::~AudioEnumerationDeltaTrigger()
{
}
//
//#############################################################################
//#############################################################################
//
void
AudioEnumerationDeltaTrigger::BuildFromPage(
PlugStream *stream,
NameList *name_list,
ClassID class_ID,
ObjectID object_ID
)
{
AudioDeltaOf<Enumeration>::BuildFromPage(
stream,
name_list,
class_ID,
object_ID
);
}
//#############################################################################
//############################# AudioIntegerTrigger #####################
//#############################################################################
//
//#############################################################################
//#############################################################################
//
AudioIntegerTrigger::AudioIntegerTrigger(
PlugStream *stream,
Entity *entity
):
AudioTriggerOf<int>(stream, entity)
{
PrimeWatcher();
}
//
//#############################################################################
//#############################################################################
//
AudioIntegerTrigger::~AudioIntegerTrigger()
{
}
//
//#############################################################################
//#############################################################################
//
void
AudioIntegerTrigger::BuildFromPage(
PlugStream *stream,
NameList *name_list,
ClassID class_ID,
ObjectID object_ID
)
{
AudioTriggerOf<int>::BuildFromPage(stream, name_list, class_ID, object_ID);
}
//
//#############################################################################
//#############################################################################
//
AudioControlValue
AudioIntegerTrigger::ExtractInterestingValue(const int *attribute_ptr)
{
Check(this);
Check_Pointer(attribute_ptr);
return *attribute_ptr;
}
//#############################################################################
//###################### AudioControlsButtonTrigger #####################
//#############################################################################
//
//#############################################################################
//#############################################################################
//
AudioControlsButtonTrigger::AudioControlsButtonTrigger(
PlugStream *stream,
Entity *entity
):
AudioTriggerOf<ControlsButton>(stream, entity)
{
PrimeWatcher();
}
//
//#############################################################################
//#############################################################################
//
AudioControlsButtonTrigger::~AudioControlsButtonTrigger()
{
}
//
//#############################################################################
//#############################################################################
//
void
AudioControlsButtonTrigger::BuildFromPage(
PlugStream *stream,
NameList *name_list,
ClassID class_ID,
ObjectID object_ID
)
{
AudioTriggerOf<ControlsButton>::BuildFromPage(
stream,
name_list,
class_ID,
object_ID
);
}
//
//#############################################################################
//#############################################################################
//
AudioControlValue
AudioControlsButtonTrigger::ExtractInterestingValue(
const ControlsButton *attribute_ptr
)
{
Check(this);
Check_Pointer(attribute_ptr);
return *attribute_ptr;
}
//#############################################################################
//########################### AudioStateWatcher #########################
//#############################################################################
//
//#############################################################################
//#############################################################################
//
AudioStateWatcher::AudioStateWatcher(
PlugStream *stream,
Entity *entity
):
AudioWatcherOf<StateIndicator>(stream, entity)
{
Check_Pointer(attributePointer);
// BRING-UP GUARD [T3, temporary]: an AudioStateWatcher binds to a StateIndicator
// attribute BY NAME. The Mech's own state indicators (SimulationState, Animation/
// ReplicantAnimationState, CollisionState) are real; but audio also binds state
// attrs on subsystems that are not fully reconstructed yet (GeneratorState,
// CondenserState, Torso MotionState, ...). Those resolve to an unconstructed
// object -- a null/garbage vtable at +0 or a debug-fill (0xCDCDCDCD) watcher chain
// at +0x18 -- and AddAudioWatcher would AV in SChainOf::Add. Skip the register
// (that subsystem's state audio stays silent) until the subsystem is built; this
// is SELF-CLEARING (a real StateIndicator passes). See docs: audio subsystem wave.
// Validate the AUDIO SOCKET at +0x18 (what AddAudioWatcher touches), NOT the
// object's +0 vtable: a real StateIndicator has a vtable at +0, but the binary's
// 0x54 subsystem alarm (GaugeAlarm54) is non-polymorphic there (a raw header) yet
// has a real, constructed SChainOf socket at +0x18. A skip means the socket is
// unconstructed (null / debug-fill 0xCDCDCDCD) -- the inert pad or a subsystem not
// yet reconstructed. Registering there would AV in SChainOf::Add.
{
unsigned chain = *(unsigned*)((char*)attributePointer + 0x18);
if (chain == 0 || chain == 0xCDCDCDCD)
{
if (getenv("BT_AUDIO_LOG"))
DEBUG_STREAM << "[audiostate] skip watcher on unbuilt StateIndicator "
<< attributePointer << " (chain=" << (void*)chain << ")\n" << std::flush;
return;
}
}
Cast_Object(StateIndicator*, attributePointer)->AddAudioWatcher(this);
}
//
//#############################################################################
//#############################################################################
//
AudioStateWatcher::~AudioStateWatcher()
{
}
//
//#############################################################################
//#############################################################################
//
void
AudioStateWatcher::SendNotificationOfChange()
{
Check(this);
StateIndicator *state_indicator = GetCurrentPointer();
Check(state_indicator);
StateChanged(
state_indicator->GetOldState(),
state_indicator->GetState()
);
}
//
//#############################################################################
//#############################################################################
//
void
AudioStateWatcher::StateChanged(
Enumeration,
Enumeration
)
{
Fail("AudioStateWatcher::StateChanged - Should never reach here");
}
//#############################################################################
//############################## AudioStateTrigger ######################
//#############################################################################
//
//#############################################################################
//#############################################################################
//
AudioStateTrigger::AudioStateTrigger(
PlugStream *stream,
Entity *entity
):
AudioStateWatcher(stream, entity)
{
MemoryStream_Read(stream, &triggerState);
MemoryStream_Read(stream, &inverseTrigger);
MemoryStream_Read(stream, &controlID);
MemoryStream_Read(stream, &controlValue);
MemoryStream_Read(stream, &excludeTransition);
MemoryStream_Read(stream, &excludeState);
if (getenv("BT_ATTRBIND_LOG")) { static int s_stc=0; if (s_stc++<120)
DEBUG_STREAM << "[statecfg] attrPtr=" << (void*)attributePointer
<< " comp=" << (void*)audioComponentSocket.GetCurrent()
<< " trigState=" << triggerState << " inv=" << (int)inverseTrigger
<< " ctl=" << (int)controlID << "/" << controlValue
<< " excl=" << (int)excludeTransition << "/" << excludeState
<< "\n" << std::flush; }
PrimeWatcher();
}
//
//#############################################################################
//#############################################################################
//
AudioStateTrigger::~AudioStateTrigger()
{
}
//
//#############################################################################
//#############################################################################
//
void
AudioStateTrigger::BuildFromPage(
PlugStream *stream,
NameList *name_list,
ClassID class_ID,
ObjectID object_ID
)
{
AudioStateWatcher::BuildFromPage(stream, name_list, class_ID, object_ID);
//
// Write standard fields
//
MEM_STRM_WRITE_ENTRY(*stream, name_list, Enumeration, trigger_state);
MEM_STRM_WRITE_ENTRY(*stream, name_list, Logical, inverse_trigger);
MEM_STRM_WRITE_ENTRY(*stream, name_list, Enumeration, control_ID);
MEM_STRM_WRITE_ENTRY(*stream, name_list, AudioControlValue, control_value);
//
// Write optional fields
//
Logical exclude_transition = False;
Enumeration exclude_state = 0;
if ((name_list)->FindData("exclude_state") != NULL)
{
exclude_transition = True;
Check_Pointer((name_list)->FindData("exclude_state"));
Convert_From_Ascii(
(const char *)(name_list)->FindData("exclude_state"),
&exclude_state
);
}
MemoryStream_Write(stream, &exclude_transition);
MemoryStream_Write(stream, &exclude_state);
}
//
//#############################################################################
//#############################################################################
//
void
AudioStateTrigger::StateChanged(
Enumeration old_state,
Enumeration new_state
)
{
Check(this);
//
// If the exclude switch is on then check for the transition
//
if (excludeTransition)
{
if (
!inverseTrigger &&
new_state == triggerState &&
old_state == excludeState
)
{
return;
}
if (
inverseTrigger &&
old_state == triggerState &&
new_state == excludeState
)
{
return;
}
}
//
// Check for the trigger state
//
if (
(!inverseTrigger && new_state == triggerState) ||
(inverseTrigger && old_state == triggerState)
)
{
Check(&audioComponentSocket);
Check(audioComponentSocket.GetCurrent());
if (triggerState >= 5 && getenv("BT_AUDIO_SPATIAL")) { static int s_sf=0; if (s_sf++<60)
DEBUG_STREAM << "[statefire] trigState=" << triggerState
<< " old=" << old_state << " new=" << new_state
<< " comp=" << (void*)audioComponentSocket.GetCurrent()
<< " compClass=" << (int)audioComponentSocket.GetCurrent()->GetClassID()
<< " ctl=" << (int)controlID << "/" << controlValue
<< "\n" << std::flush; }
#if 1
audioComponentSocket.GetCurrent()->ReceiveControl(
controlID,
controlValue
);
#else
audioComponentSocket.GetCurrent()->PostReceiveControl(
controlID,
controlValue
);
#endif
}
}