Skip to content

Instantly share code, notes, and snippets.

@tomara-x
Created June 9, 2024 04:01
Show Gist options
  • Save tomara-x/36779bc6b4bcfb4b91b9d46b06021cc9 to your computer and use it in GitHub Desktop.
Save tomara-x/36779bc6b4bcfb4b91b9d46b06021cc9 to your computer and use it in GitHub Desktop.
//! 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