using SFML.Audio; using SFML.System; namespace FluidSim.Audio { public class AudioOutputStream : SoundStream { private readonly SimulationRingBuffer _sourceBuffer; private double _speed = 1.0; // non‑volatile, accessed with Volatile.Read/Write public AudioOutputStream(SimulationRingBuffer sourceBuffer) { _sourceBuffer = sourceBuffer; // 2 channels, 44.1 kHz, stereo Initialize(2, 44100, new[] { SoundChannel.FrontLeft, SoundChannel.FrontRight }); } /// Playback speed (0.01 … 1.0 or higher for catch‑up). public double Speed { get => Volatile.Read(ref _speed); set => Volatile.Write(ref _speed, value); } protected override bool OnGetData(out short[] samples) { const int monoBlockSize = 512; float[] temp = new float[monoBlockSize]; int read = _sourceBuffer.ReadInterpolated(temp, monoBlockSize, Speed); samples = new short[monoBlockSize * 2]; if (read > 0) { for (int i = 0; i < read; i++) { float clamped = Math.Clamp(temp[i], -1f, 1f); short final = (short)(clamped * short.MaxValue); samples[i * 2] = final; // left samples[i * 2 + 1] = final; // right } } // Fill rest with silence for (int i = read * 2; i < samples.Length; i++) samples[i] = 0; return true; } protected override void OnSeek(Time timeOffset) => throw new NotSupportedException(); } }