using SFML.Audio; using SFML.System; using System; namespace Car_simulation { public class EngineSound : SoundStream { private const uint SAMPLE_RATE = 44100; private const ushort CHANNELS = 2; private const float BUFFER_DURATION = 0.01f; private volatile float _currentRPM = 800f; private volatile float _currentThrottle = 0f; private float _volume = 0.3f; private bool _isPlaying = false; private readonly int _cylinders = 4; private readonly Random _rand = 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(CHANNELS, SAMPLE_RATE); for (int i = 0; i < _harmonicPhases.Length; i++) _harmonicPhases[i] = (float)(_rand.NextDouble() * 2 * Math.PI); } public void SetEngineState(float rpm, float throttle) { _currentRPM = rpm; _currentThrottle = throttle; _volume = 0.1f + 0.4f * throttle; } public void StartSound() { if (!_isPlaying) { Play(); _isPlaying = true; } } public void StopSound() { if (_isPlaying) { Stop(); _isPlaying = false; } } protected override bool OnGetData(out short[] samples) { int sampleCount = (int)(SAMPLE_RATE * BUFFER_DURATION) * CHANNELS; samples = new short[sampleCount]; for (int i = 0; i < sampleCount; i += 2) { float sampleValue = 0f; // 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++) { 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 noise for roughness sampleValue += ((float)_rand.NextDouble() * 2f - 1f) * 0.02f * throttle; // Volume and clamp sampleValue = Math.Clamp(sampleValue * _volume, -1f, 1f); short finalSample = (short)(sampleValue * 32767); samples[i] = finalSample; samples[i + 1] = finalSample; } return true; } protected override void OnSeek(Time timeOffset) { for (int i = 0; i < _harmonicPhases.Length; i++) _harmonicPhases[i] = (float)(_rand.NextDouble() * 2 * Math.PI); } } }