#pragma once #include namespace fluidsim::port { struct AudioFrame { float left{}, right{}; }; struct Vec3 { float x{}, y{}, z{}; }; // Flow-derivative sound source used by the C# two-stroke and pulse scenarios. class SoundProcessor { public: float gain{1.0f}; explicit SoundProcessor(int sample_rate = 44100, float listener_distance = 1.0f); float process(float mass_flow_out); private: float dt_{}, scale_factor_{}, lowpass_alpha_{}, derivative_alpha_{}; float flow_lowpass_{}, previous_flow_{}, smooth_derivative_{}; }; class RadiationSource { public: float flow_tau{0.005f}, smooth_tau{0.020f}, gain{15.0f}; Vec3 pipe_end{}, pipe_axis{1.0f, 0.0f, 0.0f}; Vec3 listener{0.0f, 0.0f, 1.5f}, listener_right{1.0f, 0.0f, 0.0f}; float ear_spacing{0.18f}, head_shadow_cutoff{4000.0f}, speed_of_sound{343.0f}; float directivity_balance{0.30f}, air_absorption_exponent{0.002f}, min_air_cutoff{1500.0f}; explicit RadiationSource(int sample_rate = 44100); AudioFrame process(float mass_flow); float last_mono_pressure() const { return last_mono_pressure_; } private: class DelayLine; class OnePole; int sample_rate_{}; float dt_{}, flow_lp_{}, previous_flow_lp_{}, smooth_derivative_{}; float last_mono_pressure_{}; std::vector left_delay_, right_delay_; std::size_t write_position_{}, left_offset_{}, right_offset_{}; float air_filter_{}, shadow_left_{}, shadow_right_{}; static float dot(Vec3 a, Vec3 b); static float length(Vec3 value); static Vec3 subtract(Vec3 a, Vec3 b); float one_pole(float input, float cutoff, float& state) const; float delayed(float input, std::vector& buffer, std::size_t offset); }; } // namespace fluidsim::port