Created
June 9, 2024 04:01
-
-
Save tomara-x/36779bc6b4bcfb4b91b9d46b06021cc9 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//! process input from mic with fundsp | |
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; | |
use cpal::{FromSample, SizedSample}; | |
use fundsp::hacker32::*; | |
use crossbeam_channel::{bounded, Receiver, Sender}; | |
#[derive(Clone)] | |
pub struct InputNode { | |
lr: Receiver<f32>, | |
rr: Receiver<f32>, | |
} | |
impl InputNode { | |
pub fn new(lr: Receiver<f32>, rr: Receiver<f32>) -> Self { | |
InputNode { lr, rr } | |
} | |
} | |
impl AudioNode for InputNode { | |
const ID: u64 = 1117; | |
type Sample = f32; | |
type Inputs = U0; | |
type Outputs = U2; | |
type Setting = (); | |
#[inline] | |
fn tick( | |
&mut self, | |
_input: &Frame<Self::Sample, Self::Inputs>, | |
) -> Frame<Self::Sample, Self::Outputs> { | |
let l = self.lr.recv().unwrap_or(0.); | |
let r = self.rr.recv().unwrap_or(0.); | |
[l, r].into() | |
} | |
} | |
fn main() { | |
// sender / receiver for left and right channels (stereo mic) | |
let (ls, lr) = bounded(64); | |
let (rs, rr) = bounded(64); | |
let host = cpal::default_host(); | |
// input | |
let in_device = host.default_input_device().unwrap(); | |
let in_config = in_device.default_input_config().unwrap(); | |
match in_config.sample_format() { | |
cpal::SampleFormat::F32 => run_in::<f32>(&in_device, &in_config.into(), ls, rs), | |
cpal::SampleFormat::I16 => run_in::<i16>(&in_device, &in_config.into(), ls, rs), | |
cpal::SampleFormat::U16 => run_in::<u16>(&in_device, &in_config.into(), ls, rs), | |
format => eprintln!("unsupported sample format: {}", format), | |
} | |
// output | |
let out_device = host.default_output_device().unwrap(); | |
let out_config = out_device.default_output_config().unwrap(); | |
match out_config.sample_format() { | |
cpal::SampleFormat::F32 => run_out::<f32>(&out_device, &out_config.into(), lr, rr), | |
cpal::SampleFormat::I16 => run_out::<i16>(&out_device, &out_config.into(), lr, rr), | |
cpal::SampleFormat::U16 => run_out::<u16>(&out_device, &out_config.into(), lr, rr), | |
format => eprintln!("unsupported sample format: {}", format), | |
} | |
std::thread::sleep(std::time::Duration::from_secs(120)); | |
} | |
fn run_in<T>( | |
device: &cpal::Device, | |
config: &cpal::StreamConfig, | |
ls: Sender<f32>, | |
rs: Sender<f32>, | |
) where | |
T: SizedSample, f32: FromSample<T> | |
{ | |
let channels = config.channels as usize; | |
let err_fn = |err| eprintln!("an error occurred on stream: {}", err); | |
let stream = device.build_input_stream( | |
config, | |
move |data: &[T], _: &cpal::InputCallbackInfo| { | |
read_data(data, channels, ls.clone(), rs.clone()) | |
}, | |
err_fn, | |
None, | |
); | |
if let Ok(stream) = stream { | |
if let Ok(()) = stream.play() { | |
std::mem::forget(stream); | |
} | |
} | |
} | |
fn read_data<T>(input: &[T], channels: usize, ls: Sender<f32>, rs: Sender<f32>) | |
where | |
T: SizedSample, f32: FromSample<T> | |
{ | |
for frame in input.chunks(channels) { | |
for (channel, sample) in frame.iter().enumerate() { | |
if channel & 1 == 0 { | |
let _ = ls.send(sample.to_sample::<f32>()); | |
} else { | |
let _ = rs.send(sample.to_sample::<f32>()); | |
} | |
} | |
} | |
} | |
fn run_out<T>( | |
device: &cpal::Device, | |
config: &cpal::StreamConfig, | |
lr: Receiver<f32>, | |
rr: Receiver<f32>, | |
) where | |
T: SizedSample + FromSample<f32>, | |
{ | |
let channels = config.channels as usize; | |
let input = An(InputNode::new(lr, rr)); | |
let reverb = reverb_stereo(40., 5., 1.); | |
let chorus = chorus(0, 0.015, 0.005, 0.5) | chorus(0, 0.015, 0.005, 0.5); | |
let graph = input >> chorus >> reverb; | |
let mut graph = BlockRateAdapter32::new(Box::new(graph)); | |
graph.set_sample_rate(config.sample_rate.0 as f64); | |
let mut next_value = move || graph.get_stereo(); | |
let err_fn = |err| eprintln!("an error occurred on stream: {}", err); | |
let stream = device.build_output_stream( | |
config, | |
move |data: &mut [T], _: &cpal::OutputCallbackInfo| { | |
write_data(data, channels, &mut next_value) | |
}, | |
err_fn, | |
None, | |
); | |
if let Ok(stream) = stream { | |
if let Ok(()) = stream.play() { | |
std::mem::forget(stream); | |
} | |
} | |
} | |
fn write_data<T>(output: &mut [T], channels: usize, next_sample: &mut dyn FnMut() -> (f32, f32)) | |
where | |
T: SizedSample + FromSample<f32>, | |
{ | |
for frame in output.chunks_mut(channels) { | |
let sample = next_sample(); | |
let left = T::from_sample(sample.0); | |
let right = T::from_sample(sample.1); | |
for (channel, sample) in frame.iter_mut().enumerate() { | |
if channel & 1 == 0 { | |
*sample = left; | |
} else { | |
*sample = right; | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment