c51d64a6b85f6eb8f92da06f2e06ef5cb1a45764
7
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
a11a697824 |
Audio: footsteps arrive on the FIRST stride -- the 10-20 s warm-up bug
User report: footfalls silent for the first 10-20 s of every mission (all
mechs), then solid. Root cause was three interlocking layers, each measured
with timestamped traces:
1. The authored footstep volume chain (LocalAcceleration [0,10]->ctl100 +
LocalVelocity [0,0.6]->ctl101 through authored N=30/N=15
AudioControlSmoothers, fill 0) hangs off the SOURCE's watcher chain
(scale watches smoother watches mixer watches source), and an idle
source's chain executes only at Start attempts -- one smoother sample
per stride.
2. Each hop is frame-gated (AudioComponent::ExecuteWatchers,
DefaultAudioFrameDelay), so any burst collapses to one execution.
3. The transient drop gate (vol < 0.3, AUDREND) rejected every Start while
the smoother average crawled up 1/30th per attempt -> ~25 dropped strides
before the first audible step, then per-frame execution while playing
kept it warm forever ("solid after that").
Fixes (engine-level, each documented in place):
- AudioScaleOf<T>::Execute now sends EVERY poll (scales are continuous
value-feeders; the base bitwise change-gate -- Motion::operator== is
memcmp -- froze on our deterministic gait math, where the original's
noisy physics floats never bit-repeated. Triggers/matchers keep the
change gate: their semantics are edge-based).
- Component/AudioComponent::PrimeWatchers(passes): recursive, GATE-FREE
watcher pump; AUDREND runs 30 passes on every transient Start request so
the authored smoothers evaluate at their true steady state before the
drop gate reads the volume.
- localAcceleration derives via the binary's exact structure: 15-sample
ring buffers of the raw position derivative + dt (ctor part_012.c:9836,
derive :15169-15195), in the PerformAndWatch tail so it runs every frame.
- AttributeWatcherOf::GrabCurrentValue private -> protected (the scale
override calls it).
Verified (30 s walk from cold start): drops 25 -> 3 (the survivors are
authentic quiet-stride gating: first gentle strides at vol ~0.28 vs the 0.3
gate), footfalls deliver from the first stride, 43 delivered with live
per-stride gain variation. Diag traces added: [accwatch]/[fsscale]/
[smooth]/[smoothcfg]/[motionscalecfg] + timestamps on DROP/volset.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
691e88569a |
Audio Phase 2 (AUDIO_FIDELITY F1/F2/F13/F14): full-zone soundbank -- key-splits,
layers, baked tuning, loop regions, release fades Extractor rewrite (tools/sf2extract.py): every sample-bearing instrument zone becomes a SAMPLEINFO slot -- 603 zones / 241 presets (68/115 + 94/126 multi- zone, matching the audit exactly). Per zone: keyRange(43), sampleModes(54), SBK samplePitch(55) + rootKey(58) + coarse/fineTune(51/52), attenuation(48, INVERTED SBK scale, 0.375 dB/step [T3], baked into the PCM), releaseVolEnv(38), pan(17), shdr loop region. F2 pitch (algebraically exact): WAV rate = round(44100 * 2^(((6000 - rootCents) + tune)/1200)) -- EMU8000 v1 base 44100. Cross-checks: FootFall 17300 Hz (the audit's +4.2 st), MissileLoaded low zone 88200 (-24 st), Warnings01 8-way klaxon split w/ 3 looped zones, Death01 162 Hz extremes. F1 zone selection: SetupPatch/PlayNote take the authored note; attach/play only zones whose [keyLo,keyHi] contains it (detach the rest so a rewound source can't replay a stale buffer). Live-verified: LaserLoaded/ MissileLoaded note 36 -> low clunk zone, note 84 -> high blip zone (pitch 4); LaserCFire plays all 3 authored layers incl the looping sustain. Stereo-pair zones pan via listener-relative AL_POSITION (distance model is AL_NONE). MAX_PRESET_SAMPLES=25 (AllExplosion); fixed PRESET_isImplemented's >=5 bound. F13 loops + releases: authored [loopStart,loopEnd] applied via AL_SOFT_loop_points at buffer load (0 rejections; kills the latent boom-loop on MechExplosion's 1.5% sustain slice + the ~2.4 Hz LaserBSustain wrap tick); StopNote now honors the authored releaseVolEnv (1.1-3.9 s on ~20 looping presets) with a dB-linear fade serviced from AudioHead::Execute; restarts reclaim fading sources. One-shots keep the instant stop (faithful). Regression (40s drive+fire): stable, loop points accepted, key-splits + layers verified in the delivery trace, chirp still dead, footfalls fire. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
fc7f311771 |
Audio Phase 1 (AUDIO_FIDELITY F3/F4/F10/F15/F18/F20 + KB): the quick wins
F3 distance: alDistanceModel(AL_NONE); restored the commented authored distance multiply in Dynamic3D::CalculateSourceVolumeScale, added the same override to Static3D, dropped the (now inert) AL_MAX_DISTANCE writes. Far battle audio follows the authored knee/rolloff curve again, and the volume cull / voice steal / ducking chains are distance-aware. F4 volume law: AL_GAIN = volume_scale^2 at all 3 per-frame sites (Direct/ Dyn3D/Static3D) -- the GM CC7 squared curve; linear was ~+6 dB at mids. F10 doppler: alDopplerFactor(0) (AL's model ran wrong constants + sign- inverted velocity: approaching sources pitched DOWN); the AUTHORED dopplerCents (AUDIO.INI range/speed-of-sound model, decomp-proven consumer part_008.c:7466) now adds into the Dynamic3D pitch chain. F15 ConfigureActivePress: published at the MechSubsystem BASE (binary id 2, descriptor @0x50de5c -> +0x110); renamed the misnamed vitalSubsystemIndex member (the weapon ConfigureMappables handler already drives it 0/-1). Removed the invented Sensor/Myomers duplicates and RESTORED their byte- exact layouts (0x328/0x358 allocs + asserts). MechWeapon's pinned-id pad absorbed the +1 chain shift -- which matches the binary's own numbering. F18 cook-off warning: AmmoBin FireCountdownStarted -> the existing cookOffArmed @0x18C (binary table @0x512600); the countdown klaxon can fire. F20 zoom blip: L4MechControlsMapper publishes TargetRangeExponent -> the live @0x1a4 zoom member (own table chained to MechControlsMapper). KB: replaced the bogus divisionParameters+0x10 rate read with SystemClock::GetTicksPerSecond() (FUN_0044e19c is GetFrameRate -- original audio frames were CLOCK TICKS). Regression (35s drive+fire): stable; ConfigureActivePress binds real on all 9 subsystems (idle -1, zero pad redirects); FireCountdownStarted + TargetRangeExponent bind live members; attrnull 53 -> 41; chirp still dead; footfalls still fire (gain now correctly squared). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
37d4554440 |
Audio: implement NOTE->PITCH + control pitch -- the AL_PITCH was never applied (task #50)
The whole pitch system existed but was discarded: NoteAudioHandler captured the
MIDI note, ExecuteModel computed relativePitch from the control-chain cents --
and NOTHING ever called alSourcef(AL_PITCH). Every sample played at root.
Consequences fixed:
- The load-time 'chirping' ambience: an authored looped sequence (div=120
tempo=160, note 51 events on bank1:83) plays a rhythmic tick pitched ~9
semitones DOWN; at root it was a high click ('chirp'). Identified live via
the new BT_AUDIO_DUMP playing-source tape + user ear-match at cadence.
- Engine now revs: EngineMotor pitch rides the throttle (measured 1.0 -> 1.12).
- All sequence-authored patterns (alarms/klaxons/ambience) regain their pitch.
Implementation: BTNotePitchFactor(note) = 2^((note-60)/12); applied at all 3
patch-source StartImplementations (fresh attach) and all 3 ExecuteModels
(combined with the control-chain relativePitch, clamped 0.5..2.0 as before).
Uses the existing AudioSource::GetCurrentNoteValue().
Also: [seqcfg] sequence-config dump, g_bufferNames + BT_AUDIO_DUMP live tape
(1/s: every playing AL source with sample name/gain/pitch/loop),
tools/soundboard.py updated mapping.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
f4b2a7f87f |
Audio: fix POISONED calibration rate (raw-offset garbage) + calibrated audio clock (task #50)
Two real bugs found + fixed chasing the footstep volume: 1. BTL4Application::MakeAudioRenderer read sample_rate from a raw 1995-layout offset (divisionParameters+0x10) -- the databinding trap: it yielded -1.6e14 (measured), poisoning Renderer::calibrationRate for the ENTIRE audio system. Every AudioTime conversion (sequence event scheduling, compression curve durations, Seconds_To_Frames) was garbage. Sanity-clamp to the authored 1000 frames/sec default (BT_AUDIO_LOG logs the value). 2. AudioHead::Execute fed audioFrameCount raw OS ticks; now converts ticks -> calibrated frames via SystemClock::GetTicksPerSecond (1000ms fallback when the static isn't measured yet), so AudioTime consumers see the rate they were calibrated for. With rate=1000 + ms ticks the units now align end-to-end. Footstep status: chain verified through pulse -> matcher -> Start -> renderer; the volume mixer's two inputs receive only value-0 AudioControlSequence events even with correct timing -- the authored event VALUES need decoding next (sequence tempo/divisions ctor dump; possibly Verify()-stripped tempo garbage or the events are resets and the true volume rides another control id). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
99757a947d |
Audio: footstep chain traced to the END -- gait VINDICATED; audio clock 18x unit mismatch found (task #50)
MAJOR RESULT -- THE GAIT IS CORRECT, the enum was mislabeled: LoadLocomotionClips (binary-verified slot map) proves namedClip==&animationClips[5]: clips 6/7 = wwr/wwl (WALK cycle), 12/13 = rrr/rrl (RUN cycle -- what autodrive uses at full throttle), 10/11 = wrr/wrl, 14/15 = rwr/rwl, 16-21 = reverse set. The mech2.cpp "MechAnimationState" enum (0x3c-stride name table) does NOT match the runtime clip ids -- it is the mislabel (12/13 are NOT StandToReverse). The authored AudioStateTriggers on AnimationState (statecfg dump: states 1,2,5,8,9,10,11,14,15,16,17,20,21,22,23,26,27 -> Start) align EXACTLY with the TRANSITION clips under the true map; the CYCLING strides (6/7, 12/13) have no state triggers because the FootStep pulse attribute serves them -- our FootStep reconstruction is the authentic design. DO NOT renumber the gait. Footstep chain, fully mapped (all empirtical): pulse -> AudioMatchOf(match=1) -> Start on the FootFall DirectPatchSource -> DROPPED at volume 0. Volume path: an AudioControlMixer (2 inputs, never fed) <- an AudioControlSplitter <- AudioControlSequence timeline events. The sequences DO fire -- but only value-0 events (the tick-0 initializers); the later (real-volume) events never land. NEW ROOT-CAUSE CANDIDATE [T3]: audio clock unit mismatch. The renderer is constructed with DefaultRendererRate=30 (calibrationRate: Seconds_To_Frames = s*30) but AudioHead::Execute sets audioFrameCount = Now().ticks which advances ~550/s (measured via the [audioclock] probe) -- sequence event scheduling runs ~18x off the authored timing. Fix candidate: calibrate the audio renderer to the actual Now().ticks rate (or drive audioFrameCount at the calibrated 30/s). Also corrected en route: FUN_004b9550/95b8 are MechWeapon ConfigureMappables/ ChooseButton (mapper EnterConfiguration +0x38), NOT Myomers::ConnectToMover; +0x31c there is fireImpulse. Myomers::speedEffect has NO binary writer found beyond the ctor 1.0f (its 'mover coupling' stand-in is spurious). Diagnostics added: [statecfg] AudioStateTrigger ctor dump, [split] splitter forwards, [seqev] sequence timeline events, [audioclock] frame-rate probe. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
7b7d465e5e |
Initial commit: bt411 -- standalone Windows BattleTech (Tesla 4.10 port)
Clean, self-contained extraction of the BattleTech-specific work from the
reverse-engineering workspace -- engine + game + content + build, with nothing
from Red Planet or the raw archive dumps. Builds green (Win32) and runs the
single-player drive->animate->target->fire->damage->destroy loop out of the box.
Layout:
engine/ MUNGA + MUNGA_L4 shared 2007 engine, carrying our BT render/loader
work (bgfload/L4D3D/L4VIDEO: BSL bit-slice decode, LOD/ground/shadow
models) + image codec; the minimal rp/ headers the audio HAL needs
game/ reconstructed BT logic + surviving-original BT source + fwd shims
+ WinMain launcher
content/ full runtime tree (BTL4.RES, VIDEO/, GAUGE/, AUDIO/, eggs, BTDPL.INI)
docs/ format specs + reconstruction ledgers
reference/ raw Ghidra pseudocode (recon source-of-truth) + decomp exporter
tools/ MP console emulator + map/resource scanners
One top-level CMake builds munga_engine lib + bt410_l4 game lib + btl4.exe.
All paths relativized (186 fwd shims + ~437 CMake abs paths -> repo-relative);
DXSDK is the one external, overridable via -DDXSDK. Verified: builds to a
byte-identical 2.27MB exe and runs combat (TARGET DESTROYED, 0 crashes) against
the bundled content.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|