use super::SCRATCH_FRAMES;
use crate::dsl::SoundDoc;
use crate::streaming::StreamGraph;
pub trait AudioSource {
fn fill(&mut self, out: &mut [f32]) -> usize;
fn reset(&mut self) {}
}
pub fn write_interleaved(data: &mut [f32], channels: usize, stereo: &[f32]) {
let channels = channels.max(1);
let frames = (data.len() / channels).min(stereo.len() / 2);
for f in 0..frames {
let (l, r) = (stereo[f * 2], stereo[f * 2 + 1]);
let base = f * channels;
if channels == 1 {
data[base] = 0.5 * (l + r);
} else {
data[base] = l;
data[base + 1] = r;
for c in 2..channels {
data[base + c] = 0.0;
}
}
}
}
pub struct StreamSource {
graph: StreamGraph,
scratch: Vec<f32>,
gain: f32,
}
impl StreamSource {
pub fn from_doc(doc: &SoundDoc) -> Option<Self> {
let graph = StreamGraph::try_from_doc(doc)?;
let mut probe = StreamGraph::try_from_doc(doc)?;
let mut remaining =
((doc.duration.clamp(0.0, 600.0) * doc.sample_rate as f32).ceil() as usize).max(1);
let mut block = [0.0f32; 1024];
let mut peak = 0.0f32;
while remaining > 0 {
let take = block.len().min(remaining);
probe.fill(&mut block[..take]);
peak = block[..take].iter().fold(peak, |m, x| m.max(x.abs()));
remaining -= take;
}
let gain = if peak > crate::dsp::CEIL {
crate::dsp::CEIL / peak
} else {
1.0
};
Some(StreamSource {
graph,
scratch: vec![0.0; SCRATCH_FRAMES],
gain,
})
}
}
impl AudioSource for StreamSource {
fn fill(&mut self, out: &mut [f32]) -> usize {
let frames = out.len() / 2;
if self.scratch.len() < frames {
self.scratch.resize(frames, 0.0);
}
let mono = &mut self.scratch[..frames];
self.graph.fill(mono);
for f in 0..frames {
let v = mono[f] * self.gain;
out[f * 2] = v;
out[f * 2 + 1] = v;
}
frames
}
}