use crate::synthesis::automation::Automation;
use crate::track::PRIORITY_MODULATION;
const DEFAULT_SAMPLE_RATE: f32 = 44100.0;
#[derive(Debug, Clone)]
pub struct AutoPan {
pub rate: f32, pub depth: f32, pub priority: u8, phase: f32,
rate_automation: Option<Automation>,
depth_automation: Option<Automation>,
}
impl AutoPan {
pub fn with_sample_rate(rate: f32, depth: f32, _sample_rate: f32) -> Self {
Self {
rate: rate.max(0.01),
depth: depth.clamp(0.0, 1.0),
priority: PRIORITY_MODULATION,
phase: 0.0,
rate_automation: None,
depth_automation: None,
}
}
pub fn new(rate: f32, depth: f32) -> Self {
Self::with_sample_rate(rate, depth, DEFAULT_SAMPLE_RATE)
}
pub fn with_priority(mut self, priority: u8) -> Self {
self.priority = priority;
self
}
pub fn with_rate_automation(mut self, automation: Automation) -> Self {
self.rate_automation = Some(automation);
self
}
pub fn with_depth_automation(mut self, automation: Automation) -> Self {
self.depth_automation = Some(automation);
self
}
#[inline]
pub fn get_pan_offset(&mut self, sample_rate: f32, time: f32, sample_count: u64) -> f32 {
if sample_count & 63 == 0 {
if let Some(auto) = &self.rate_automation {
self.rate = auto.value_at(time).max(0.01);
}
if let Some(auto) = &self.depth_automation {
self.depth = auto.value_at(time).clamp(0.0, 1.0);
}
}
if self.depth < 0.0001 {
return 0.0;
}
let lfo = (self.phase * 2.0 * std::f32::consts::PI).sin();
self.phase += self.rate / sample_rate;
if self.phase >= 1.0 {
self.phase -= 1.0;
}
lfo * self.depth
}
pub fn reset(&mut self) {
self.phase = 0.0;
}
pub fn slow() -> Self {
Self::new(0.25, 0.75)
}
pub fn classic() -> Self {
Self::new(0.5, 0.75)
}
pub fn fast() -> Self {
Self::new(2.0, 0.75)
}
pub fn subtle() -> Self {
Self::new(0.3, 0.4)
}
pub fn extreme() -> Self {
Self::new(1.0, 1.0)
}
}