using SFML.Graphics; using SFML.Window; using SFML.System; using System.Diagnostics; using FluidSim.Core; namespace FluidSim; public class Program { private const int SampleRate = 44100; private const double DrawFrequency = 60.0; private static Scenario scenario; // Speed control (existing + new throttle) private static double desiredSpeed = 0.01; private static double currentSpeed = desiredSpeed; private const double MinSpeed = 0.0001; private const double MaxSpeed = 1.0; private const double ScrollFactor = 1.1; private static double lastDesiredSpeed = 0.1; private static bool isRealTime = false; // Throttle smoothing private static double targetThrottle = 0.0; // 1.0 when W is pressed, 0.0 otherwise private static double currentThrottle = 0.0; private const double ThrottleSmoothing = 20.0; // rate of change private static volatile bool running = true; public static void Main() { var mode = new VideoMode(new Vector2u(1280, 720)); var window = new RenderWindow(mode, "FluidSim - Engine (W = throttle)"); window.SetVerticalSyncEnabled(true); window.Closed += (_, _) => { running = false; window.Close(); }; window.MouseWheelScrolled += OnMouseWheel; window.KeyPressed += OnKeyPressed; var soundEngine = new SoundEngine(bufferCapacity: 16384); soundEngine.Volume = 100; soundEngine.Start(); scenario = new EngineScenario(); scenario.Initialize(SampleRate); var stopwatch = Stopwatch.StartNew(); double lastDrawTime = 0.0; double drawInterval = 1.0 / DrawFrequency; double lastSpeedUpdateTime = stopwatch.Elapsed.TotalSeconds; var simBuffer = new List(4096); double readIndex = 0.0; for (int i = 0; i < 4; i++) simBuffer.Add(scenario.Process()); long totalSimSteps = simBuffer.Count; long totalOutputSamples = 0; const int outputChunk = 256; float[] outputBuf = new float[outputChunk]; while (window.IsOpen) { window.DispatchEvents(); double currentRealTime = stopwatch.Elapsed.TotalSeconds; double dtClock = currentRealTime - lastSpeedUpdateTime; lastSpeedUpdateTime = currentRealTime; // Smooth simulation speed double speedSmoothing = 8.0; currentSpeed += (desiredSpeed - currentSpeed) * (1.0 - Math.Exp(-speedSmoothing * dtClock)); // ---- THROTTLE INPUT ---- targetThrottle = Keyboard.IsKeyPressed(Keyboard.Key.W) ? 1.0 : 0.0; currentThrottle += (targetThrottle - currentThrottle) * (1.0 - Math.Exp(-ThrottleSmoothing * dtClock)); // Push to engine scenario (if it's an EngineScenario) if (scenario is EngineScenario engine) engine.Throttle = currentThrottle; // Generate audio double targetAudioClock = currentRealTime + 0.05; while (totalOutputSamples < targetAudioClock * SampleRate && running) { int toGenerate = (int)Math.Min( (long)outputChunk, (long)(targetAudioClock * SampleRate) - totalOutputSamples ); if (toGenerate <= 0) break; double maxIndex = readIndex + (toGenerate - 1) * currentSpeed + 2; int requiredSimIndex = (int)Math.Ceiling(maxIndex); while (simBuffer.Count - 1 < requiredSimIndex) { simBuffer.Add(scenario.Process()); totalSimSteps++; } for (int i = 0; i < toGenerate; i++) { int i0 = (int)readIndex; int i1 = i0 + 1; double frac = readIndex - i0; float y0 = simBuffer[Math.Clamp(i0, 0, simBuffer.Count - 1)]; float y1 = simBuffer[Math.Clamp(i1, 0, simBuffer.Count - 1)]; outputBuf[i] = (float)(y0 + (y1 - y0) * frac); readIndex += currentSpeed; while (readIndex >= 1.0 && simBuffer.Count > 2) { simBuffer.RemoveAt(0); readIndex -= 1.0; } } int accepted = soundEngine.WriteSamples(outputBuf, toGenerate); totalOutputSamples += accepted; if (accepted < toGenerate) break; } // Drawing & title if (currentRealTime - lastDrawTime >= drawInterval) { double actualSpeed = totalOutputSamples / (currentRealTime * SampleRate); double simTime = totalSimSteps / (double)SampleRate; string toggleHint = isRealTime ? "[Space] slow mo" : "[Space] real time"; string throttleHint = Keyboard.IsKeyPressed(Keyboard.Key.W) ? "W held" : "W released"; window.SetTitle( $"{toggleHint} {throttleHint} " + $"Thr: {currentThrottle:F2} " + $"Speed: {currentSpeed:F3}x → {desiredSpeed:F3}x " + $"Act: {actualSpeed:F2}x" ); window.Clear(Color.Black); scenario.Draw(window); window.Display(); lastDrawTime = currentRealTime; } } soundEngine.Dispose(); window.Dispose(); } // (Mouse wheel, space toggle unchanged) private static void OnMouseWheel(object? sender, MouseWheelScrollEventArgs e) { bool wasRealTime = Math.Abs(desiredSpeed - 1.0) < 1e-6; if (e.Delta > 0) desiredSpeed *= ScrollFactor; else if (e.Delta < 0) desiredSpeed /= ScrollFactor; desiredSpeed = Math.Clamp(desiredSpeed, MinSpeed, MaxSpeed); if (!wasRealTime || Math.Abs(desiredSpeed - 1.0) > 1e-6) lastDesiredSpeed = desiredSpeed; isRealTime = Math.Abs(desiredSpeed - 1.0) < 1e-6; } private static void OnKeyPressed(object? sender, KeyEventArgs e) { if (e.Code == Keyboard.Key.Space) { if (isRealTime) desiredSpeed = lastDesiredSpeed; else desiredSpeed = 1.0; isRealTime = !isRealTime; } } }