use std::collections::VecDeque;
use std::fs::File;
use std::path::Path;
use fft_convolver::FFTConvolver;
use symphonia::core::formats::probe::Hint;
use symphonia::core::formats::{FormatOptions, TrackType};
use symphonia::core::io::MediaSourceStream;
use symphonia::core::meta::MetadataOptions;
#[derive(Debug, Clone, PartialEq)]
pub struct Route {
pub ir: Vec<f32>,
pub inputs: Vec<(usize, f32)>,
pub outputs: Vec<(usize, f32)>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Impulse {
pub rate: u32,
pub channels: Option<usize>,
pub routes: Vec<Route>,
pub in_delays: Vec<usize>,
pub out_delays: Vec<usize>,
}
impl Impulse {
pub fn from_channels(rate: u32, mut channels: Vec<Vec<f32>>) -> Self {
if channels.len() == 1 {
return Self {
rate,
channels: None,
routes: vec![Route {
ir: channels.remove(0),
inputs: vec![(0, 1.0)],
outputs: vec![(0, 1.0)],
}],
in_delays: Vec::new(),
out_delays: Vec::new(),
};
}
let n = channels.len();
Self {
rate,
channels: Some(n),
routes: channels
.into_iter()
.enumerate()
.map(|(c, ir)| Route {
ir,
inputs: vec![(c, 1.0)],
outputs: vec![(c, 1.0)],
})
.collect(),
in_delays: Vec::new(),
out_delays: Vec::new(),
}
}
pub fn fits(&self, channels: usize) -> bool {
self.channels.is_none_or(|n| n == channels)
}
pub(crate) fn routes_for(&self, channels: usize) -> Vec<Route> {
match self.channels {
Some(_) => self.routes.clone(),
None => (0..channels)
.map(|c| Route {
ir: self.routes[0].ir.clone(),
inputs: vec![(c, 1.0)],
outputs: vec![(c, 1.0)],
})
.collect(),
}
}
pub fn as_channels(&self) -> Option<Vec<&[f32]>> {
if !self
.in_delays
.iter()
.chain(&self.out_delays)
.all(|&d| d == 0)
{
return None;
}
if self.channels.is_none() {
return Some(vec![&self.routes[0].ir]);
}
let mut out = vec![None; self.channels?];
for r in &self.routes {
let ([(i, wi)], [(o, wo)]) = (&r.inputs[..], &r.outputs[..]) else {
return None;
};
if i != o || *wi != 1.0 || *wo != 1.0 || out[*i].is_some() {
return None;
}
out[*i] = Some(&r.ir[..]);
}
out.into_iter().collect()
}
pub fn mixes(&self) -> bool {
self.routes.iter().any(|r| {
r.inputs.len() > 1
|| r.outputs.len() > 1
|| r.inputs.first().map(|i| i.0) != r.outputs.first().map(|o| o.0)
})
}
pub fn delay(&self) -> usize {
let mut best = (0, 0.0f32);
for r in &self.routes {
for (i, &v) in r.ir.iter().enumerate() {
if v.abs() > best.1 {
best = (i, v.abs());
}
}
}
best.0
}
pub fn taps(&self) -> usize {
self.routes.iter().map(|r| r.ir.len()).max().unwrap_or(0)
}
}
pub fn read_audio(path: &Path) -> Result<(u32, Vec<Vec<f32>>), String> {
let file = File::open(path).map_err(|e| e.to_string())?;
let mss = MediaSourceStream::new(Box::new(file), Default::default());
let mut hint = Hint::new();
if let Some(ext) = path.extension().and_then(|e| e.to_str()) {
hint.with_extension(ext);
}
let mut reader = symphonia::default::get_probe()
.probe(
&hint,
mss,
FormatOptions::default(),
MetadataOptions::default(),
)
.map_err(|e| e.to_string())?;
let track = reader
.default_track(TrackType::Audio)
.ok_or("no audio track")?;
let track_id = track.id;
let params = track
.codec_params
.as_ref()
.and_then(|p| p.audio())
.ok_or("no audio track")?;
let rate = params.sample_rate.ok_or("no sample rate")?;
let mut decoder = symphonia::default::get_codecs()
.make_audio_decoder(params, &Default::default())
.map_err(|e| e.to_string())?;
let mut interleaved = Vec::new();
let mut packet_samples: Vec<f32> = Vec::new();
let mut channels = 0;
while let Some(packet) = reader.next_packet().map_err(|e| e.to_string())? {
if packet.track_id != track_id {
continue;
}
let decoded = decoder.decode(&packet).map_err(|e| e.to_string())?;
channels = decoded.spec().channels().count();
decoded.copy_to_vec_interleaved(&mut packet_samples);
interleaved.extend_from_slice(&packet_samples);
}
if channels == 0 || interleaved.is_empty() {
return Err("empty".into());
}
let planar = (0..channels)
.map(|c| {
interleaved
.iter()
.skip(c)
.step_by(channels)
.copied()
.collect()
})
.collect();
Ok((rate, planar))
}
struct DelayLine(VecDeque<f64>);
impl DelayLine {
fn new(frames: usize) -> Option<Self> {
(frames > 0).then(|| Self(std::iter::repeat_n(0.0, frames).collect()))
}
fn run(&mut self, samples: &mut [f64]) {
for s in samples {
self.0.push_back(*s);
*s = self.0.pop_front().expect("never empty");
}
}
}
struct Running {
conv: FFTConvolver<f64>,
inputs: Vec<(usize, f32)>,
outputs: Vec<(usize, f32)>,
}
pub(crate) struct Convolve {
routes: Vec<Running>,
channels: usize,
in_delays: Vec<Option<DelayLine>>,
out_delays: Vec<Option<DelayLine>>,
delay: usize,
skip: usize,
planar_in: Vec<Vec<f64>>,
planar_out: Vec<Vec<f64>>,
mix: Vec<f64>,
wet: Vec<f64>,
}
impl Convolve {
pub(crate) fn new(impulse: &Impulse, channels: usize) -> Self {
let routes = impulse
.routes_for(channels)
.into_iter()
.map(|r| {
let mut conv = FFTConvolver::default();
let block = (r.ir.len().next_power_of_two() / 32).clamp(1024, 16384);
let ir: Vec<f64> = r.ir.iter().map(|&t| t as f64).collect();
let _ = conv.init(block, &ir);
Running {
conv,
inputs: r.inputs,
outputs: r.outputs,
}
})
.collect();
let delays = |d: &[usize]| {
(0..channels)
.map(|c| DelayLine::new(d.get(c).copied().unwrap_or(0)))
.collect()
};
let delay = impulse.delay();
Self {
routes,
channels,
in_delays: delays(&impulse.in_delays),
out_delays: delays(&impulse.out_delays),
delay,
skip: delay,
planar_in: vec![Vec::new(); channels],
planar_out: vec![Vec::new(); channels],
mix: Vec::new(),
wet: Vec::new(),
}
}
pub(crate) fn run(&mut self, buf: &mut Vec<f64>) {
let ch = self.channels;
let frames = buf.len() / ch;
for (c, plane) in self.planar_in.iter_mut().enumerate() {
plane.clear();
plane.extend(buf.iter().skip(c).step_by(ch));
if let Some(d) = self.in_delays[c].as_mut() {
d.run(plane);
}
}
for plane in &mut self.planar_out {
plane.clear();
plane.resize(frames, 0.0);
}
self.mix.resize(frames, 0.0);
self.wet.resize(frames, 0.0);
for Running {
conv,
inputs,
outputs,
} in &mut self.routes
{
self.mix.fill(0.0);
for &(c, w) in inputs.iter() {
if let Some(plane) = self.planar_in.get(c) {
for (m, s) in self.mix.iter_mut().zip(plane) {
*m += w as f64 * s;
}
}
}
if conv.process(&self.mix, &mut self.wet).is_err() {
continue;
}
for &(c, w) in outputs.iter() {
if let Some(plane) = self.planar_out.get_mut(c) {
for (o, s) in plane.iter_mut().zip(&self.wet) {
*o += w as f64 * s;
}
}
}
}
for (c, plane) in self.planar_out.iter_mut().enumerate() {
if let Some(d) = self.out_delays[c].as_mut() {
d.run(plane);
}
for (d, s) in buf.iter_mut().skip(c).step_by(ch).zip(plane.iter()) {
*d = *s;
}
}
let drop = self.skip.min(frames);
self.skip -= drop;
buf.drain(..drop * ch);
}
pub(crate) fn flush(&mut self, dst: &mut Vec<f64>) {
let mut tail = vec![0.0; self.delay * self.channels];
self.run(&mut tail);
dst.extend_from_slice(&tail);
}
}