ProjectEcho
Physics-based spatial audio system. Applies Sabine-based RT60 reverberation, wall-blocking LPF, diffraction paths, and HRTF-based spatial audio in real time. Supports low, medium, and high quality tiers.
CHANGELOG
Changelog
1.0.6
Bug Fixes
- Fixed
Vector3.Distance(origin, origin)always returning 0 in multizone blend calculation — blend ratio now correctly scales with the open space distance (20%–40% range) - Fixed
FindShipLoopcoroutine leaking onOnDestroy— coroutine reference is now stored and stopped properly - Fixed
PropagationEnginestatic event duplicate subscription on scene reload — events are now unsubscribed before re-subscribing inAwake() - Fixed cumulative volume attenuation bug —
AudioSource.volumeis no longer multiplied cumulatively on eachPlay()call. Original volume is preserved and occlusion is applied as a ratio against it - Fixed
EntranceTriggerHooksilently failing when LC updates rename internal fields — now tries multiple field name candidates and logs a clear warning if none are found
Improvements
PlayerTrackerreflection search now has a hard limit of 20 attempts — prevents infinite retry loop in edge casesEntranceTriggerHooknow also checksNonPublicbinding flags forisEntranceToBuilding— improves compatibility with future LC buildsHRTFControlleris now properly registered as a component on startup (was defined but never attached)DebugModeconfig description updated to accurately reflect its current behavior (verbose BepInEx logging)
Code Cleanup
- Removed dead
DoScan()method fromRoomAnalyzer - Removed unused
IsInsideFacilityproperty fromPlayerTracker - Removed unused
LC_ENTRANCE_TRIGGERconstant fromPlayerTracker - Removed
DebugHUD.cs— HUD rendering was abandoned; file has been deleted
1.0.5
New Features
- Multizone blending — when standing near a doorway or spatial boundary, Project Echo now detects both the near and far spaces and blends their reverb profiles (65% primary / 35% secondary). Transitions between rooms feel natural instead of snapping.
- Reflection material filtering — virtual early reflection sources now apply per-material EQ to the reflected audio:
- Metal / Glass → high-pass emphasis, bright and sharp ringing
- Concrete / Brick → neutral reflection
- Carpet / Dirt → low-pass heavy, dark and muffled reflection
Optimizations
AudioFilterController.Update()now skips entirely when the source is not playing (isPlayingcheck) — sources at rest cost zero CPU- Lerp settlement detection: once all filter parameters reach their targets,
_settled = truestopsUpdate()from running until new values arrive AudioSourceClassifiercache is now permanent per-scene instead of 30-second TTL — reflection-based lookups happen only once per source per sceneOcclusionProcessoradds a frame-level shared cache — duplicate raycasts for the same source within the same frame are eliminatedFilterPoolaccess time is only written back when 0.5s has elapsed — reduces unnecessary dictionary writes
1.0.4
New Features
- Sound source categorization — all
AudioSourceinstances are now classified into 7 categories (Footstep, Monster, Machine, Walkie-Talkie, Player, Ambient, General). Each category receives different reverb multipliers, decay times, and occlusion strength:- Machine / Ambient: longer reverb tails to fill the space
- Footstep: shorter reverb, low-pass HPF disabled to preserve floor thud
- Monster: occlusion strength ×1.3 — stays audible but muffled through walls
- Walkie-talkie: no reverb, band-limited to 300–3400 Hz
- Precise air absorption (ISO 9613-1 approximation) — distance-based HF roll-off now uses a squared curve instead of linear. Far sounds lose their high frequencies first; low frequencies travel further, matching physical reality.
DecayHFRatiodynamic calculation — each room scan now computes a weighted average HF decay ratio from wall materials:- Metal / Glass →
1.9(HF decays slowly → bright ringing) - Concrete →
0.6(HF decays fast → dark, heavy tail) - Carpet →
0.2(near-dead, HF absorbed almost instantly)
- Metal / Glass →
MaterialHardness+AudioHighPassFilter— a per-source HPF is attached and its cutoff/resonance is driven by surface hardness. Hard surfaces produce bright, sharp early reflections; soft surfaces produce none.- Wet/Dry dynamic ratio —
dryLevelandreverbLevelnow scale with room volume. Small rooms are direct-sound dominant; large caverns fill with reverb. - Directional early reflection simulation — a virtual
AudioSourceis placed at the nearest wall reflection point and plays the source clip at low volume with aPlayDelayed(distance / 343f)offset, giving early reflections a physical direction.
Fixes
- Removed the dead
var primaryProfile = yield return StartCoroutine(...)line inRoomAnalyzerthat caused a CS1002 build error
1.0.3
New Features
- First-spawn instant scan —
RoomAnalyzernow waits for the player to reach a valid position and fires an immediate scan on first spawn, eliminating the reverb-less window after map entry - Facility entrance/exit detection —
EntranceTriggerHookpatchesEntranceTeleport.TeleportPlayer(). Entering the facility triggers an instantForceScan(); exiting forces the Outdoor profile with a 3× speed transition blend - Ceiling height correction — the room scan extracts ceiling distance from the upward raycast. Low ceilings (< 3 m) shorten RT₆₀ × 0.75; high ceilings (> 6 m) extend it × 1.25
- Corridor shape detection — if one horizontal axis is more than 3× longer than the other, the space is classified as a corridor. Diffusion is reduced and RT₆₀ shortened to produce a flutter-echo character
- Floor material weighting — a dedicated downward raycast samples the floor material at ×2 weight in the Sabine calculation, since flooring has the largest acoustic influence in a real room
Fixes
SpatialAudioManager.IsAlive()static method added — replacesInstance == nullchecks inProjectEchoPluginandSceneHookto avoid triggering the null-warning log on legitimate startup paths- Ship interior (
HangarShipcollider) is now detected at runtime; when the player is inside, theShipInteriorpreset is forced regardless of scan results - Wall layer detection now logs the resolved layer names on startup for easier debugging
Config
- New entries in
ProjectEcho.cfg:ReverbIntensity(default1.0) — reverb strength multiplierOcclusionStrength(default1.0) — occlusion LPF depth multiplierRoomTransitionSpeed(default3.5) — room blend speedDebugMode(defaultfalse) — reserved for future use
1.0.2
- Fixed reverb effect being barely noticeable due to incorrect layer mask
"Wall"layer does not exist in Lethal Company — switched to actual LC layers (Default,Room,Colliders, etc.)
- Fixed most in-game sound effects being skipped by the spatial hook
- LC sets many AudioSources to
spatialBlend = 0(2D) — non-BGM/UI sources are now forced to 3D
- LC sets many AudioSources to
- Significantly increased reverb intensity across all room presets
roomandreverbLevelvalues were too low to be audible in-game
- Fixed
[SAM] Instance not yet initializedwarning on startupSceneHook.Register()was being called beforeSpatialAudioManagerwas created
- Trigger an immediate room scan on scene load for faster reverb response on map entry
1.0.1
- Fixed CS8618: uninitialized non-nullable fields across multiple files
- Fields set in
Awake()/Bind()marked with= null! - Fields that can genuinely be null marked as nullable (
?)
- Fields set in
- Fixed CS0120:
AudioListener.transformcalled in a static context (HRTFController)- Replaced with
FindObjectOfType<AudioListener>()with caching
- Replaced with
- Fixed CS0070:
PropagationPortalcallingevent.Invoke()from outside the declaring class- Added
RaisePortalRegistered()/RaisePortalUnregistered()relay methods toPropagationEngine
- Added
- Fixed CS8600:
PropagationPortal best = nullassigned to non-nullable type - Fixed CS8603:
FilterPool.GetOrCreate()returning null from a non-nullable return type
1.0.0
- Initial release
- Real-time RT₆₀ reverb calculation using Sabine's Formula
- Physical occlusion with Low-pass filter
- Portal-based sound diffraction (doors and windows)
- HRTF spatialization with elevation and azimuth tonal shaping
- Low / Medium / High quality tiers
- Automatic low-end detection — systems with under 4 GB RAM default to Low tier