enginerenderer 0.0.1

A zero-dependency offline rendering engine in pure Rust — CPU path tracing, BVH acceleration, 16-band spectral rendering, PBR materials, animation & video export.
Documentation
use std::io::Write;

pub const AUDIO_RING_CAPACITY: usize = 8192;

pub struct AudioDevice {
    pub sample_rate: u32,
    pub buffer_size: usize,
    ring: Vec<[f32; 2]>,
    write_head: usize,
    read_head: usize,
    frames_written: u64,
}

impl AudioDevice {
    pub fn new(sample_rate: u32, buffer_size: usize) -> Self {
        let capacity = AUDIO_RING_CAPACITY.max(buffer_size * 4);
        Self {
            sample_rate,
            buffer_size,
            ring: vec![[0.0; 2]; capacity],
            write_head: 0,
            read_head: 0,
            frames_written: 0,
        }
    }

    pub fn capacity(&self) -> usize {
        self.ring.len()
    }

    pub fn available_frames(&self) -> usize {
        let cap = self.ring.len();
        (self.write_head + cap - self.read_head) % cap
    }

    pub fn push_frame(&mut self, frame: [f32; 2]) {
        self.ring[self.write_head] = frame;
        self.write_head = (self.write_head + 1) % self.ring.len();
        self.frames_written += 1;
    }

    pub fn fill_from_stereo(&mut self, samples: &[[f32; 2]]) {
        for &s in samples {
            self.push_frame(s);
        }
    }

    pub fn drain_period(&mut self) -> Vec<[f32; 2]> {
        let count = self.available_frames().min(self.buffer_size);
        let mut out = Vec::with_capacity(count);
        for _ in 0..count {
            out.push(self.ring[self.read_head]);
            self.read_head = (self.read_head + 1) % self.ring.len();
        }
        out
    }

    pub fn write_period_pcm16<W: Write>(&mut self, writer: &mut W) -> std::io::Result<usize> {
        let period = self.drain_period();
        let mut bytes_written = 0;
        for [l, r] in &period {
            let li = (*l * 32767.0).clamp(-32768.0, 32767.0) as i16;
            let ri = (*r * 32767.0).clamp(-32768.0, 32767.0) as i16;
            writer.write_all(&li.to_le_bytes())?;
            writer.write_all(&ri.to_le_bytes())?;
            bytes_written += 4;
        }
        Ok(bytes_written)
    }

    pub fn latency_ms(&self) -> f64 {
        self.available_frames() as f64 / self.sample_rate.max(1) as f64 * 1000.0
    }

    pub fn frames_written(&self) -> u64 {
        self.frames_written
    }
}