using Sandbox.Audio; namespace Sandbox; /// /// Apply DSP to mixer when listener is inside a DspVolume /// [Expose] sealed class DspVolumeGameSystem : GameObjectSystem { public DspVolumeGameSystem( Scene scene ) : base( scene ) { Listen( Stage.FinishUpdate, 0, Update, "Dsp Update" ); } public override void Dispose() { base.Dispose(); var gameMixer = Mixer.FindMixerByName( "Game" ); if ( gameMixer is null ) return; foreach ( var processor in _entries.Values ) { gameMixer.RemoveProcessor( processor.processor ); } } HashSet _active { get; set; } record class Entry( DspProcessor processor, bool active ); Dictionary _entries = new(); void Update() { if ( Scene.IsEditor ) return; var gameMixer = Mixer.FindMixerByName( "Game" ); if ( gameMixer is null ) return; int lastPriority = int.MinValue; string found = default; foreach ( var volume in Scene.Volumes.FindAll( Sound.Listener.Position ) ) { int priority = volume.Priority; if ( priority < lastPriority ) continue; lastPriority = priority; found = volume.Dsp.Name; } if ( !string.IsNullOrWhiteSpace( found ) && !_entries.ContainsKey( found ) ) { var processor = new DspProcessor(); processor.Effect = found; processor.Mix = 0; gameMixer.AddProcessor( processor ); _entries[found] = new Entry( processor, true ); } foreach ( var entry in _entries ) { var mixTarget = found == entry.Key ? 1 : 0; entry.Value.processor.Mix = entry.Value.processor.Mix.Approach( mixTarget, Time.Delta ); } foreach ( var entry in _entries.Where( x => x.Value.processor.Mix <= 0 ).ToArray() ) { gameMixer.RemoveProcessor( entry.Value.processor ); _entries.Remove( entry.Key ); } } private void TryAdd( string name ) { if ( _active.Contains( name ) ) return; _active.Add( name ); } private void TryRemove( string name ) { if ( !_active.Contains( name ) ) return; _active.Remove( name ); } }