zk-audio 0.1.0

Audio processing library for voice recording and enhancement
Documentation
use crate::core::{AudioError, AudioFrame, AudioResult, AudioSpec};
use crate::mic_sim::contracts::MicrophoneSimProcessor;
use crate::types::{AudioBlock, ProcessContext};

#[allow(dead_code)]
pub struct MicrophoneSimPipeline {
    spec: AudioSpec,
    frame_size: usize,
    frame_index: u64,
    processors: Vec<Box<dyn MicrophoneSimProcessor>>,
}

#[allow(dead_code)]
impl MicrophoneSimPipeline {
    pub fn new(
        spec: AudioSpec,
        mut processors: Vec<Box<dyn MicrophoneSimProcessor>>,
    ) -> AudioResult<Self> {
        let frame_size = (spec.sample_rate as usize / 100)
            .max(160)
            .next_power_of_two();
        let prepare_ctx = ProcessContext {
            spec,
            frame_size,
            frame_index: 0,
        };
        for processor in &mut processors {
            let _ = processor.prepare_with_context(&prepare_ctx)?;
        }
        Ok(Self {
            spec,
            frame_size,
            frame_index: 0,
            processors,
        })
    }

    pub fn process_frame(&mut self, frame: &mut AudioFrame) -> AudioResult<()> {
        if frame.spec.sample_rate != self.spec.sample_rate
            || frame.spec.channels != self.spec.channels
        {
            return Err(AudioError::new(
                "Microphone simulation received frame with unexpected spec",
            ));
        }

        let mut block = AudioBlock::new(std::mem::take(&mut frame.samples), frame.spec);
        let ctx = ProcessContext {
            spec: self.spec,
            frame_size: self.frame_size,
            frame_index: self.frame_index,
        };
        for processor in &mut self.processors {
            processor.process_block(&mut block, &ctx)?;
        }
        self.frame_index += 1;
        frame.samples = block.samples;
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::core::AudioSpec;
    use crate::mic_sim::contracts::MicrophoneSimProcessor;

    struct MultiplyProcessor(f32);

    impl MicrophoneSimProcessor for MultiplyProcessor {
        fn name(&self) -> &'static str {
            "multiply"
        }

        fn prepare(&mut self, _spec: AudioSpec) -> AudioResult<()> {
            Ok(())
        }

        fn process(&mut self, frame: &mut AudioFrame) -> AudioResult<()> {
            for sample in &mut frame.samples {
                *sample *= self.0;
            }
            Ok(())
        }
    }

    #[test]
    fn microphone_sim_pipeline_processes_audio() {
        let mut pipeline = MicrophoneSimPipeline::new(
            AudioSpec {
                sample_rate: 48_000,
                channels: 1,
            },
            vec![Box::new(MultiplyProcessor(0.5))],
        )
        .unwrap();

        let mut frame = AudioFrame {
            samples: vec![0.8, -0.4],
            spec: AudioSpec {
                sample_rate: 48_000,
                channels: 1,
            },
        };
        pipeline.process_frame(&mut frame).unwrap();

        assert_eq!(frame.samples, vec![0.4, -0.2]);
    }
}