use crate::buffer::AudioBuffer;
pub use garjan::weather::{Rain, RainIntensity};
pub use garjan::weather::Thunder;
pub use garjan::weather::Wind;
pub use garjan::fire::Fire;
pub use garjan::water::{Water, WaterType};
pub use garjan::surf::{Surf, SurfIntensity};
pub use garjan::underwater::{Underwater, UnderwaterDepth};
pub use garjan::bubble::Bubble;
pub use garjan::impact::{Impact, ImpactType};
pub use garjan::friction::Friction;
pub use garjan::rolling::Rolling;
pub use garjan::footstep::Footstep;
pub use garjan::creak::Creak;
pub use garjan::texture::{AmbientTexture, TextureType};
pub use garjan::foliage::Foliage;
pub use garjan::insect::Insect;
pub use garjan::cloth::Cloth;
pub use garjan::whistle::Whistle;
pub use garjan::whoosh::Whoosh;
pub use garjan::wingflap::{BirdSize, WingFlap};
pub use garjan::material::Material;
pub use garjan::modal::{ExcitationType, Exciter, ModalBank, ModePattern, ModeSpec};
pub use garjan::lod::Quality;
pub use garjan::contact::{
CreakSource, FoliageType, FrictionType, MovementType, RollingBody, Terrain,
};
pub use garjan::error::GarjanError;
pub fn render_environment<S: EnvironmentSynth>(
synth: &mut S,
sample_rate: u32,
duration: f32,
) -> crate::Result<AudioBuffer> {
let samples = synth
.synthesize(duration)
.map_err(|e| crate::NadaError::Dsp(format!("environment synthesis failed: {e}")))?;
AudioBuffer::from_interleaved(samples, 1, sample_rate)
.map_err(|e| crate::NadaError::Dsp(format!("buffer from environment output: {e}")))
}
pub trait EnvironmentSynth {
fn synthesize(&mut self, duration: f32) -> std::result::Result<Vec<f32>, GarjanError>;
}
macro_rules! impl_env_synth {
($($ty:ty),* $(,)?) => {
$(
impl EnvironmentSynth for $ty {
#[inline]
fn synthesize(&mut self, duration: f32) -> std::result::Result<Vec<f32>, GarjanError> {
self.synthesize(duration)
}
}
)*
};
}
impl_env_synth!(
Rain,
Thunder,
Wind,
Fire,
Water,
Surf,
Underwater,
Bubble,
Friction,
Rolling,
Footstep,
Creak,
AmbientTexture,
Foliage,
Insect,
Cloth,
Whistle,
Whoosh,
WingFlap,
);
pub fn render_impact(
impact: &mut Impact,
impact_type: ImpactType,
sample_rate: u32,
) -> crate::Result<AudioBuffer> {
let samples = impact
.synthesize(impact_type)
.map_err(|e| crate::NadaError::Dsp(format!("impact synthesis failed: {e}")))?;
AudioBuffer::from_interleaved(samples, 1, sample_rate)
.map_err(|e| crate::NadaError::Dsp(format!("buffer from impact output: {e}")))
}
pub fn render_block(
synth: &mut impl EnvironmentBlock,
frames: usize,
sample_rate: u32,
) -> AudioBuffer {
let mut samples = vec![0.0_f32; frames];
synth.process_block(&mut samples);
AudioBuffer::from_interleaved(samples, 1, sample_rate)
.unwrap_or_else(|_| AudioBuffer::silence(1, frames, sample_rate.max(1)))
}
pub trait EnvironmentBlock {
fn process_block(&mut self, output: &mut [f32]);
}
macro_rules! impl_env_block {
($($ty:ty),* $(,)?) => {
$(
impl EnvironmentBlock for $ty {
#[inline]
fn process_block(&mut self, output: &mut [f32]) {
self.process_block(output);
}
}
)*
};
}
impl_env_block!(
Rain,
Thunder,
Wind,
Fire,
Water,
Surf,
Underwater,
Bubble,
Friction,
Rolling,
Footstep,
Creak,
AmbientTexture,
Foliage,
Insect,
Cloth,
Whistle,
Whoosh,
WingFlap,
);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rain_synthesis() {
let mut rain = Rain::new(RainIntensity::Heavy, 44100.0).unwrap();
let buf = render_environment(&mut rain, 44100, 0.5).unwrap();
assert!(buf.frames() > 0);
assert!(buf.samples().iter().all(|s| s.is_finite()));
}
#[test]
fn wind_synthesis() {
let mut wind = Wind::new(15.0, 0.5, 44100.0).unwrap();
let buf = render_environment(&mut wind, 44100, 0.5).unwrap();
assert!(buf.frames() > 0);
assert!(buf.rms() > 0.0);
}
#[test]
fn thunder_synthesis() {
let mut thunder = Thunder::new(500.0, 44100.0).unwrap();
let buf = render_environment(&mut thunder, 44100, 0.5).unwrap();
assert!(buf.frames() > 0);
}
#[test]
fn fire_synthesis() {
let mut fire = Fire::new(0.7, 44100.0).unwrap();
let buf = render_environment(&mut fire, 44100, 0.5).unwrap();
assert!(buf.frames() > 0);
assert!(buf.samples().iter().all(|s| s.is_finite()));
}
#[test]
fn impact_synthesis() {
let mut impact = Impact::new(Material::Wood, 44100.0).unwrap();
let buf = render_impact(&mut impact, ImpactType::Strike, 44100).unwrap();
assert!(buf.frames() > 0);
}
#[test]
fn render_block_streaming() {
let mut rain = Rain::new(RainIntensity::Light, 44100.0).unwrap();
let buf = render_block(&mut rain, 4410, 44100);
assert_eq!(buf.frames(), 4410);
assert!(buf.samples().iter().all(|s| s.is_finite()));
}
}