89 lines
4.0 KiB
C++
89 lines
4.0 KiB
C++
#pragma once
|
|
|
|
#include "FluidSim/AudioProcessing.hpp"
|
|
#include "FluidSim/OutdoorExhaustReverb.hpp"
|
|
#include <array>
|
|
#include <atomic>
|
|
#include <cstddef>
|
|
#include <vector>
|
|
|
|
namespace fluidsim::port {
|
|
enum class OutdoorScene { Dry, Field, Wall, Woodland, Tunnel };
|
|
const char *scene_name(OutdoorScene scene);
|
|
|
|
struct OutdoorAudioConfig {
|
|
Vec3 listener{1.6f, 1.0f, 2.0f};
|
|
Vec3 listener_right{1.0f, 0.0f, 0.0f};
|
|
Vec3 intake_position{-0.2f, 0.7f, 0.0f};
|
|
Vec3 exhaust_position{0.55f, 0.5f, 0.0f};
|
|
Vec3 second_exhaust_position{-0.55f, 0.5f, 0.0f};
|
|
Vec3 valve_position{0.08f,0.42f,0.0f};
|
|
bool dual_exhaust{};
|
|
bool valve_contact_enabled{};
|
|
float ear_spacing{0.18f}, sound_speed{343.0f};
|
|
float ground_y{0.0f}, wall_x{-1.5f};
|
|
float ground_reflection{0.35f}, wall_reflection{0.65f};
|
|
float pressure_gain{0.005f}; // 200 Pa per device full scale, not speaker SPL.
|
|
float monitor_cutoff_hz{6000.0f}; // Second-order acquisition/rendering filter.
|
|
};
|
|
|
|
// Moving geometric direct/first-order reflection paths plus an explicitly
|
|
// approximate diffuse tail. No feedback into the fluid/combustion simulation.
|
|
class OutdoorAudio {
|
|
public:
|
|
static constexpr std::array<const char*,4> source_names{"intake", "exhaust", "exhaust-2", "valve-contact"};
|
|
struct SourceFrames { AudioFrame rendered{}, dry{}, unfiltered{}; };
|
|
const auto& source_frames() const { return sources_; }
|
|
AudioFrame tail_frame() const { return last_tail_; }
|
|
void set_source_enabled(std::size_t index,bool enabled) { enabled_.at(index)=enabled; }
|
|
explicit OutdoorAudio(int sample_rate = 44100, OutdoorAudioConfig config = {});
|
|
// Acoustic source inputs are ambient-density times mouth volume flow, kg/s.
|
|
// Fluid transport still uses actual donor-density mass flux independently.
|
|
AudioFrame process(float intake_mass_flow, float exhaust_mass_flow, float second_exhaust_mass_flow = 0, float valve_contact_mass_flow = 0);
|
|
void set_scene(OutdoorScene scene) { requested_scene_.store(scene, std::memory_order_relaxed); }
|
|
OutdoorScene scene() const { return requested_scene_.load(std::memory_order_relaxed); }
|
|
// Call on the simulation thread or under the scenario's simulation lock.
|
|
void set_listener(AudioListener listener);
|
|
void set_source_positions(const std::array<Vec3,3>& positions);
|
|
void set_monitor_filter(bool enabled) { monitor_filter_ = enabled; } // Set before playback/capture.
|
|
AudioFrame dry_frame() const { return last_dry_; }
|
|
AudioFrame unfiltered_frame() const { return last_raw_; }
|
|
|
|
private:
|
|
struct LowPass {
|
|
float b0{}, b1{}, b2{}, a1{}, a2{}, z1{}, z2{};
|
|
float process(float x);
|
|
};
|
|
struct Path {
|
|
float delay_left{}, delay_right{}, gain_left{}, gain_right{}, alpha{1};
|
|
float target_delay_left{}, target_delay_right{}, target_gain_left{}, target_gain_right{};
|
|
float state_left{}, state_right{};
|
|
int group{}; // direct, ground, wall, trees, tunnel
|
|
};
|
|
struct Emitter {
|
|
std::vector<float> filtered, raw;
|
|
std::vector<Path> paths;
|
|
std::size_t position{};
|
|
float previous_flow{};
|
|
float center_delay{}, target_center_delay{}, center_gain{}, target_center_gain{};
|
|
LowPass filter;
|
|
};
|
|
int rate_{};
|
|
OutdoorAudioConfig config_;
|
|
std::atomic<OutdoorScene> requested_scene_{OutdoorScene::Wall};
|
|
OutdoorScene active_scene_{OutdoorScene::Wall};
|
|
bool monitor_filter_{true};
|
|
std::array<Emitter, 4> emitters_;
|
|
OutdoorExhaustReverb late_reverb_;
|
|
std::array<float, 5> weights_{1, 0, 0, 0, 0};
|
|
float scene_alpha_{}, tail_mix_{};
|
|
float geometry_alpha_{}, reverb_input_{};
|
|
AudioFrame last_dry_{}, last_raw_{}, last_tail_{};
|
|
std::array<SourceFrames,4> sources_{};
|
|
std::array<bool,4> enabled_{true,true,true,true};
|
|
void configure_emitter(Emitter &emitter, Vec3 position);
|
|
void update_geometry();
|
|
AudioFrame render(Emitter &emitter, float flow, AudioFrame &dry, AudioFrame &raw);
|
|
};
|
|
} // namespace fluidsim::port
|