Files
Car-Simulation/Car simulation/EngineSound.cs
2026-01-14 17:29:48 +01:00

111 lines
3.4 KiB
C#

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);
}
}
}