echolot 0.1.1

A way to quickly share files over sound
#![allow(clippy::precedence)]
#![allow(dead_code)]

use cpal::{traits::{DeviceTrait, HostTrait, StreamTrait}, Device, SupportedStreamConfig, StreamConfig};

pub fn semitone_freq(n: u32) -> f32 {
    440.0 * f32::powf(f32::powf(2.0, 1.0 / 12.0), n as f32)
}

fn write_data<T>(output: &mut [T], channels: usize, next_sample: &mut dyn FnMut() -> f32)
where
    T: cpal::Sample,
{
    for frame in output.chunks_mut(channels) {
        let value: T = cpal::Sample::from::<f32>(&next_sample());
        for sample in frame.iter_mut() {
            *sample = value;
        }
    }
}

pub struct Speaker {
    device: Device,
    config: SupportedStreamConfig,
}

impl Speaker {
    pub fn new() -> Result<Self, std::io::Error> {
        let host = cpal::default_host();
        let device = host.default_output_device().expect("failed to find a default output device");
        let config = device.default_output_config().unwrap();

        Ok(Self {
            device,
            config,
        })
    }

    pub fn play_freq(&mut self, frequency: f32, length: u64) -> Result<(), anyhow::Error> {
        let sample_rate = self.config.sample_rate().0 as f32;
        let channels = self.config.channels() as usize;

        let mut sample_clock = 0f32;
        let mut next_value = move || {
            sample_clock = (sample_clock + 1.0) % sample_rate;
            (sample_clock * frequency * 2.0 * std::f32::consts::PI / sample_rate).sin()
        };

    
        let err_fn = |err| eprintln!("an error occurred on stream: {}", err);

        let stream = self.device.build_output_stream(
            &StreamConfig::from(self.config.to_owned()),
            move |data: &mut [f32], _: &cpal::OutputCallbackInfo| {
                write_data(data, channels, &mut next_value)
            },
            err_fn,
        )?;
        stream.play()?;
    
        std::thread::sleep(std::time::Duration::from_millis(length));
    
        Ok(())
    }
}