SignalLost-ProjectEcho icon

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 FindShipLoop coroutine leaking on OnDestroy — coroutine reference is now stored and stopped properly
  • Fixed PropagationEngine static event duplicate subscription on scene reload — events are now unsubscribed before re-subscribing in Awake()
  • Fixed cumulative volume attenuation bug — AudioSource.volume is no longer multiplied cumulatively on each Play() call. Original volume is preserved and occlusion is applied as a ratio against it
  • Fixed EntranceTriggerHook silently failing when LC updates rename internal fields — now tries multiple field name candidates and logs a clear warning if none are found

Improvements

  • PlayerTracker reflection search now has a hard limit of 20 attempts — prevents infinite retry loop in edge cases
  • EntranceTriggerHook now also checks NonPublic binding flags for isEntranceToBuilding — improves compatibility with future LC builds
  • HRTFController is now properly registered as a component on startup (was defined but never attached)
  • DebugMode config description updated to accurately reflect its current behavior (verbose BepInEx logging)

Code Cleanup

  • Removed dead DoScan() method from RoomAnalyzer
  • Removed unused IsInsideFacility property from PlayerTracker
  • Removed unused LC_ENTRANCE_TRIGGER constant from PlayerTracker
  • 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 (isPlaying check) — sources at rest cost zero CPU
  • Lerp settlement detection: once all filter parameters reach their targets, _settled = true stops Update() from running until new values arrive
  • AudioSourceClassifier cache is now permanent per-scene instead of 30-second TTL — reflection-based lookups happen only once per source per scene
  • OcclusionProcessor adds a frame-level shared cache — duplicate raycasts for the same source within the same frame are eliminated
  • FilterPool access time is only written back when 0.5s has elapsed — reduces unnecessary dictionary writes

1.0.4

New Features

  • Sound source categorization — all AudioSource instances 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.
  • DecayHFRatio dynamic 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)
  • 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 ratiodryLevel and reverbLevel now scale with room volume. Small rooms are direct-sound dominant; large caverns fill with reverb.
  • Directional early reflection simulation — a virtual AudioSource is placed at the nearest wall reflection point and plays the source clip at low volume with a PlayDelayed(distance / 343f) offset, giving early reflections a physical direction.

Fixes

  • Removed the dead var primaryProfile = yield return StartCoroutine(...) line in RoomAnalyzer that caused a CS1002 build error

1.0.3

New Features

  • First-spawn instant scanRoomAnalyzer now 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 detectionEntranceTriggerHook patches EntranceTeleport.TeleportPlayer(). Entering the facility triggers an instant ForceScan(); 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 — replaces Instance == null checks in ProjectEchoPlugin and SceneHook to avoid triggering the null-warning log on legitimate startup paths
  • Ship interior (HangarShip collider) is now detected at runtime; when the player is inside, the ShipInterior preset 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 (default 1.0) — reverb strength multiplier
    • OcclusionStrength (default 1.0) — occlusion LPF depth multiplier
    • RoomTransitionSpeed (default 3.5) — room blend speed
    • DebugMode (default false) — 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
  • Significantly increased reverb intensity across all room presets
    • room and reverbLevel values were too low to be audible in-game
  • Fixed [SAM] Instance not yet initialized warning on startup
    • SceneHook.Register() was being called before SpatialAudioManager was 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 (?)
  • Fixed CS0120: AudioListener.transform called in a static context (HRTFController)
    • Replaced with FindObjectOfType<AudioListener>() with caching
  • Fixed CS0070: PropagationPortal calling event.Invoke() from outside the declaring class
    • Added RaisePortalRegistered() / RaisePortalUnregistered() relay methods to PropagationEngine
  • Fixed CS8600: PropagationPortal best = null assigned 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