velox/Code/Utils/EngineStream/EngineStreamPlayer.cs
2025-12-01 00:02:14 +07:00

121 lines
3.1 KiB
C#

using Sandbox;
using Sandbox.Audio;
using System;
using System.Collections.Generic;
using VeloX.Audio;
using static VeloX.EngineStream;
namespace VeloX;
public class EngineStreamPlayer( EngineStream stream ) : IDisposable
{
private static readonly Mixer EngineMixer = Mixer.FindMixerByName( "Engine" );
public EngineStream Stream { get; set; } = stream;
public EngineState EngineState { get; set; }
public bool EngineSoundPaused => EngineState != EngineState.Running;
public float Throttle { get; set; }
public bool IsRedlining { get; set; }
public float RPMPercent { get; set; }
private float _wobbleTime;
public readonly Dictionary<Layer, SoundHandle> EngineSounds = [];
public void Update( float deltaTime, Vector3 position, bool isLocal = false )
{
var globalPitch = 1.0f;
// Gear wobble effect
if ( _wobbleTime > 0 )
{
_wobbleTime -= deltaTime * (0.1f + Throttle);
globalPitch += MathF.Cos( _wobbleTime * Stream.Parameters.WobbleFrequency ) * _wobbleTime * (1 - _wobbleTime) * Stream.Parameters.WobbleStrength;
}
globalPitch *= Stream.Parameters.Pitch;
// Redline effect
var redlineVolume = 1.0f;
if ( IsRedlining )
{
redlineVolume = 1 - Stream.Parameters.RedlineStrength +
MathF.Cos( RealTime.Now * Stream.Parameters.RedlineFrequency ) *
Stream.Parameters.RedlineStrength;
}
// Process layers
foreach ( var (id, layer) in Stream.Layers )
{
EngineSounds.TryGetValue( layer, out var channel );
if ( !channel.IsValid() && layer.AudioPath.IsValid() )
{
channel = Sound.PlayFile( layer.AudioPath );
EngineSounds[layer] = channel;
}
if ( !channel.IsValid() || channel.Paused && (EngineSoundPaused || layer.IsMuted) )
continue;
// Reset controller outputs
float layerVolume = 1.0f;
float layerPitch = 1.0f;
// Apply all controllers
foreach ( var controller in layer.Controllers )
{
var inputValue = controller.InputParameter switch
{
Controller.InputTypes.Throttle => Throttle,
Controller.InputTypes.RpmFraction => RPMPercent,
_ => 0.0f
};
var normalized = Math.Clamp( inputValue, controller.InputRange.Min, controller.InputRange.Max );
var outputValue = controller.InputRange.Remap(
normalized,
controller.OutputRange.Min,
controller.OutputRange.Max
);
// Apply to correct parameter
switch ( controller.OutputParameter )
{
case Controller.OutputTypes.Volume:
layerVolume *= outputValue;
break;
case Controller.OutputTypes.Pitch:
layerPitch *= outputValue;
break;
}
}
// Apply redline effect if needed
layerVolume *= layer.UseRedline ? redlineVolume : 1.0f;
layerPitch *= globalPitch;
// Update audio channel
channel.Pitch = layerPitch;
channel.Volume = layerVolume * Stream.Parameters.Volume;
channel.Position = position;
channel.ListenLocal = isLocal;
channel.Paused = EngineSoundPaused || layer.IsMuted;
channel.FollowParent = true;
channel.TargetMixer = EngineMixer;
}
}
public void Dispose()
{
foreach ( var item in EngineSounds )
{
item.Value?.Stop();
item.Value?.Dispose();
}
}
}