diff --git a/Car simulation/Drivetrain.cs b/Car simulation/Drivetrain.cs index 48f5965..4dd7d71 100644 --- a/Car simulation/Drivetrain.cs +++ b/Car simulation/Drivetrain.cs @@ -22,8 +22,8 @@ public float ClutchEngagement { get; set; } = 0f; // 0 = disengaged, 1 = fully engaged // Clutch properties - public float MaxClutchTorque { get; set; } = 4500f; - public float ClutchStiffness { get; set; } = 50f; // Softer spring + public float MaxClutchTorque { get; set; } = 400f; + public float ClutchStiffness { get; set; } = 50f; // State public float ClutchTorque { get; private set; } diff --git a/Car simulation/Engine.cs b/Car simulation/Engine.cs index 430c1c0..bf40f4f 100644 --- a/Car simulation/Engine.cs +++ b/Car simulation/Engine.cs @@ -13,7 +13,7 @@ // Physical properties public float MomentOfInertia { get; set; } = 0.25f; public float IdleRPM { get; set; } = 800f; - public float RevLimit { get; set; } = 12000; + public float RevLimit { get; set; } = 7000; public float StallSpeed { get; set; } = 200f; public float Throttle { get; set; } = 0f; public bool IsRunning => RPM > StallSpeed; @@ -23,14 +23,15 @@ // Torque curve public Dictionary TorqueCurve { get; set; } = new() { - { 0f, 0f }, - { 800f, 200f }, - { 2000f, 300 }, - { 4500f, 300f }, - { 6800f, 300 }, - { 7200f, 350 }, - { 11000f, 600f }, - { 12500, 500f }, + { 0f, 0f }, + { 800f, 95f }, + { 1500f, 160f }, + { 2500f, 200f }, + { 4000f, 235f }, // peak torque + { 5000f, 230f }, + { 6000f, 210f }, + { 6800f, 185f }, + { 7200f, 170f }, }; public Engine() diff --git a/Car simulation/EngineSound.cs b/Car simulation/EngineSound.cs index 71bae3b..55b5ecb 100644 --- a/Car simulation/EngineSound.cs +++ b/Car simulation/EngineSound.cs @@ -6,54 +6,34 @@ namespace Car_simulation { public class EngineSound : SoundStream { - // Audio properties - smaller buffer for less latency private const uint SAMPLE_RATE = 44100; - private const ushort CHANNEL_COUNT = 2; // Stereo - private const float BUFFER_DURATION = 0.05f; // 10ms instead of 50ms! + private const ushort CHANNELS = 2; + private const float BUFFER_DURATION = 0.01f; - // Engine sound properties - NO SMOOTHING for instant response - private volatile float _currentRPM = 800f; // volatile for thread safety + private volatile float _currentRPM = 800f; private volatile float _currentThrottle = 0f; private float _volume = 0.3f; private bool _isPlaying = false; - // Harmonic series - DIRECT RPM TO FREQUENCY - private float[] _harmonicRatios = { 1f, 2f, 3f, 5f }; - private float[] _harmonicAmplitudes = { 0.2f, 0.5f, 0.35f, 0.2f }; - private float[] _harmonicPhases = new float[4]; + private readonly int _cylinders = 4; + private readonly Random _rand = new Random(); - // Engine configuration - for directa RPM calculation - public int CylinderCount { get; set; } = 4; - public float FiringFrequencyMultiplier => CylinderCount / 2f; // 4-stroke engines - - // For RPM to frequency mapping - private float _rpmToHzFactor; - - private Random _random = new Random(); + // Engine harmonics + private readonly float[] _harmonicMultiples = { 1f, 2f, 3f, 4f }; + private readonly float[] _harmonicAmps = { 0.3f, 0.6f, 0.4f, 0.2f }; + private readonly float[] _harmonicPhases = new float[4]; public EngineSound() { - Initialize(CHANNEL_COUNT, SAMPLE_RATE); - - // Calculate direct conversion factor - // RPM to Hz: (RPM / 60) × (Cylinders / 2) for 4-stroke - _rpmToHzFactor = (1f / 60f) * (CylinderCount / 2f); - - // Initialize phases + Initialize(CHANNELS, SAMPLE_RATE); for (int i = 0; i < _harmonicPhases.Length; i++) - { - _harmonicPhases[i] = (float)(_random.NextDouble() * 2 * Math.PI); - } + _harmonicPhases[i] = (float)(_rand.NextDouble() * 2 * Math.PI); } - // CALL THIS FROM YOUR PHYSICS THREAD - INSTANT UPDATE public void SetEngineState(float rpm, float throttle) { - // NO LOCK, NO SMOOTHING - DIRECT ASSIGNMENT _currentRPM = rpm; _currentThrottle = throttle; - - // Volume based on throttle (instant) _volume = 0.1f + 0.4f * throttle; } @@ -77,59 +57,45 @@ namespace Car_simulation protected override bool OnGetData(out short[] samples) { - // SMALLER BUFFER: 10ms instead of 50ms - int sampleCount = (int)(SAMPLE_RATE * BUFFER_DURATION) * 2; // *2 for stereo + int sampleCount = (int)(SAMPLE_RATE * BUFFER_DURATION) * CHANNELS; samples = new short[sampleCount]; - // Get current values ONCE per buffer (not per sample) - float rpm = _currentRPM; - float throttle = _currentThrottle; - float volume = _volume; - - // DIRECT RPM TO FREQUENCY - NO SMOOTHING - float baseFrequency = rpm * _rpmToHzFactor; // (RPM/60) × (cylinders/2) - - // Pre-calculate harmonic frequencies - float[] harmonicFrequencies = new float[_harmonicRatios.Length]; - float[] phaseIncrements = new float[_harmonicRatios.Length]; - - for (int h = 0; h < _harmonicRatios.Length; h++) - { - harmonicFrequencies[h] = baseFrequency * _harmonicRatios[h]; - phaseIncrements[h] = harmonicFrequencies[h] * 2f * MathF.PI / SAMPLE_RATE; - } - - // Calculate roughness factor - float roughness = 0.02f * throttle; - - // Generate sound for (int i = 0; i < sampleCount; i += 2) { float sampleValue = 0f; - // Sum all harmonics - for (int h = 0; h < _harmonicRatios.Length; h++) + // Read RPM per sample (instant) + float rpm = _currentRPM; + float throttle = _currentThrottle; + + // Base frequency for 4-cylinder, 4-stroke + float baseFreq = (rpm / 60f) * (_cylinders / 2f); + + // Sum harmonics + for (int h = 0; h < _harmonicMultiples.Length; h++) { - sampleValue += MathF.Sin(_harmonicPhases[h]) * _harmonicAmplitudes[h]; - _harmonicPhases[h] += phaseIncrements[h]; + float freq = baseFreq * _harmonicMultiples[h]; + float phaseInc = freq * 2f * MathF.PI / SAMPLE_RATE; + + // Slight jitter for realism + float jitter = 0.001f * ((float)_rand.NextDouble() - 0.5f); + _harmonicPhases[h] += phaseInc + jitter; if (_harmonicPhases[h] > 2f * MathF.PI) _harmonicPhases[h] -= 2f * MathF.PI; + + sampleValue += MathF.Sin(_harmonicPhases[h]) * _harmonicAmps[h]; } - // Add roughness - sampleValue += (float)(_random.NextDouble() * 2 - 1) * roughness; + // Add noise for roughness + sampleValue += ((float)_rand.NextDouble() * 2f - 1f) * 0.02f * throttle; - // Apply volume - sampleValue *= volume; + // Volume and clamp + sampleValue = Math.Clamp(sampleValue * _volume, -1f, 1f); + short finalSample = (short)(sampleValue * 32767); - // Clamp and convert - sampleValue = Math.Clamp(sampleValue, -1f, 1f); - short sample = (short)(sampleValue * 32767); - - // Stereo - samples[i] = sample; - samples[i + 1] = sample; + samples[i] = finalSample; + samples[i + 1] = finalSample; } return true; @@ -137,11 +103,8 @@ namespace Car_simulation protected override void OnSeek(Time timeOffset) { - // Reset phases for (int i = 0; i < _harmonicPhases.Length; i++) - { - _harmonicPhases[i] = (float)(_random.NextDouble() * 2 * Math.PI); - } + _harmonicPhases[i] = (float)(_rand.NextDouble() * 2 * Math.PI); } } -} \ No newline at end of file +}