use crate::dsp::audio_buffer::{AudioBuffer, AudioEffectState, AudioSettings};
use crate::dsp::bands::NUM_BANDS;
use bitflags::bitflags;
use std::cmp::PartialEq;
use crate::effects::eq::{EqEffect, EqEffectParameters};
use crate::effects::gain::{GainEffect, GainEffectParameters};
use crate::simulators::direct::DirectSoundPath;
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DirectApplyFlags: u8 {
const DistanceAttenuation = 1 << 0;
const AirAbsorption = 1 << 1;
const Directivity = 1 << 2;
const Occlusion = 1 << 3;
const Transmission = 1 << 4;
const Delay = 1 << 5;
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TransmissionType {
FrequencyIndependent,
FrequencyDependent,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct DirectEffectParameters {
pub direct_sound_path: DirectSoundPath,
pub flags: DirectApplyFlags,
pub transmission_type: TransmissionType,
}
pub struct DirectEffect {
pub frame_size: usize,
eq_effect: EqEffect,
gain_effect: GainEffect,
}
impl DirectEffect {
pub fn new(audio_settings: AudioSettings) -> Self {
Self {
frame_size: audio_settings.frame_size,
eq_effect: EqEffect::new(audio_settings),
gain_effect: GainEffect::new(audio_settings),
}
}
#[expect(dead_code)]
pub(crate) fn reset(&mut self) {
self.eq_effect.reset();
self.gain_effect.reset();
}
pub fn apply(
&mut self,
parameters: DirectEffectParameters,
input: &AudioBuffer<1>,
output: &mut AudioBuffer<1>,
) -> AudioEffectState {
let mut gain: f32 = 0.0;
let mut eq_coefficients: [f32; NUM_BANDS] = [0.0, 0.0, 0.0];
let mut buf: AudioBuffer<1> = AudioBuffer::new(input[0].len());
Self::calculate_gain_and_eq(
parameters.direct_sound_path,
parameters.flags,
parameters.transmission_type,
&mut gain,
&mut eq_coefficients,
);
let air_absorption = (parameters.flags & DirectApplyFlags::AirAbsorption).bits() != 0;
let transmission = (parameters.flags & DirectApplyFlags::Transmission).bits() != 0;
let transmission_freq_dep =
parameters.transmission_type == TransmissionType::FrequencyDependent;
let apply_eq = air_absorption || (transmission && transmission_freq_dep);
let gain_parameters = GainEffectParameters { gain };
if apply_eq {
let eq_parameters = EqEffectParameters {
gains: eq_coefficients,
};
self.eq_effect.apply(eq_parameters, input, &mut buf);
self.gain_effect.apply(gain_parameters, &buf, output);
} else {
self.gain_effect.apply(gain_parameters, input, output);
}
AudioEffectState::TailComplete
}
#[expect(dead_code)]
pub(crate) fn tail(output: &mut AudioBuffer<1>) -> AudioEffectState {
output.make_silent();
AudioEffectState::TailComplete
}
fn calculate_gain_and_eq(
direct_path: DirectSoundPath,
flags: DirectApplyFlags,
transmission_type: TransmissionType,
overall_gain: &mut f32,
eq_coefficients: &mut [f32; NUM_BANDS],
) {
*overall_gain = match flags & DirectApplyFlags::DistanceAttenuation {
DirectApplyFlags::DistanceAttenuation => direct_path.distance_attenuation,
_ => 1.0,
};
for i in 0..NUM_BANDS {
if (flags & DirectApplyFlags::AirAbsorption).bits() != 0 {
eq_coefficients[i] = direct_path.air_absorption[i];
} else {
eq_coefficients[i] = 1.0;
}
}
if (flags & DirectApplyFlags::Directivity).bits() != 0 {
*overall_gain *= direct_path.directivity;
}
if ((flags & DirectApplyFlags::AirAbsorption).bits() != 0)
|| (((flags & DirectApplyFlags::Transmission).bits() != 0)
&& transmission_type == TransmissionType::FrequencyDependent)
{
EqEffect::normalize_gains(eq_coefficients, overall_gain);
}
if (flags & DirectApplyFlags::Occlusion).bits() == 0 {
return;
}
if (flags & DirectApplyFlags::Transmission).bits() != 0 {
match transmission_type {
TransmissionType::FrequencyIndependent => {
let mut average_transmission_factor = 0.0;
for transmission in direct_path.transmission {
average_transmission_factor += transmission;
}
average_transmission_factor /= NUM_BANDS as f32;
*overall_gain *= direct_path.occlusion
+ (1.0 - direct_path.occlusion) * average_transmission_factor;
}
TransmissionType::FrequencyDependent => {
for i in 0..NUM_BANDS {
eq_coefficients[i] *= direct_path.occlusion
+ (1.0 - direct_path.occlusion) * direct_path.transmission[i];
}
}
};
} else {
*overall_gain *= direct_path.occlusion;
}
}
}