Add project files.

This commit is contained in:
max
2025-12-18 01:04:21 +01:00
parent 532b1f4c53
commit c22452c66c
11 changed files with 1138 additions and 0 deletions

View File

@@ -0,0 +1,149 @@
using SFML.Audio;
using SFML.System;
using System;
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.01f; // 10ms instead of 50ms!
// Engine sound properties - NO SMOOTHING for instant response
private volatile float _currentRPM = 800f; // volatile for thread safety
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, 4f, 6f };
private float[] _harmonicAmplitudes = { 1f, 0.3f, 0.1f, 0.05f };
private float[] _harmonicPhases = new float[4];
// Engine configuration - for direct 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();
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
for (int i = 0; i < _harmonicPhases.Length; i++)
{
_harmonicPhases[i] = (float)(_random.NextDouble() * 2 * Math.PI);
}
Console.WriteLine($"EngineSound initialized: {BUFFER_DURATION * 1000:F0}ms buffer, {CylinderCount} cylinders");
}
// 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;
}
public void StartSound()
{
if (!_isPlaying)
{
Play();
_isPlaying = true;
}
}
public void StopSound()
{
if (_isPlaying)
{
Stop();
_isPlaying = false;
}
}
protected override bool OnGetData(out short[] samples)
{
// SMALLER BUFFER: 10ms instead of 50ms
int sampleCount = (int)(SAMPLE_RATE * BUFFER_DURATION) * 2; // *2 for stereo
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++)
{
sampleValue += MathF.Sin(_harmonicPhases[h]) * _harmonicAmplitudes[h];
_harmonicPhases[h] += phaseIncrements[h];
if (_harmonicPhases[h] > 2f * MathF.PI)
_harmonicPhases[h] -= 2f * MathF.PI;
}
// Add roughness
sampleValue += (float)(_random.NextDouble() * 2 - 1) * roughness;
// Apply volume
sampleValue *= volume;
// Clamp and convert
sampleValue = Math.Clamp(sampleValue, -1f, 1f);
short sample = (short)(sampleValue * 32767);
// Stereo
samples[i] = sample;
samples[i + 1] = sample;
}
return true;
}
protected override void OnSeek(Time timeOffset)
{
// Reset phases
for (int i = 0; i < _harmonicPhases.Length; i++)
{
_harmonicPhases[i] = (float)(_random.NextDouble() * 2 * Math.PI);
}
}
}
}