Skip to content

Instantly share code, notes, and snippets.

@ayyybe
Last active July 4, 2023 06:52
Show Gist options
  • Save ayyybe/d696ead1b82d4de7ea5a8fe8e38fe93f to your computer and use it in GitHub Desktop.
Save ayyybe/d696ead1b82d4de7ea5a8fe8e38fe93f to your computer and use it in GitHub Desktop.
rust cpal + dasp + channel = realtime audio processing
// live recording and realtime resampling of any input to 16kHz mono for whisper
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
use dasp::{interpolate::sinc::Sinc, ring_buffer, signal, Frame, Signal};
use std::{sync::mpsc, thread};
fn get_voicemeeter_input_device(host: &cpal::Host) -> cpal::Device {
host.input_devices()
.expect("Failed to get input devices")
.find(|device| {
device
.name()
.unwrap()
.to_lowercase()
.contains("voicemeeter")
})
.expect("Failed to find input device")
}
fn sinc_resample(
signal: impl Signal<Frame = f32>,
source_hz: f64,
target_hz: f64,
) -> impl Signal<Frame = f32> {
let ring_buffer = ring_buffer::Fixed::from([f32::EQUILIBRIUM; 10]);
let sinc = Sinc::new(ring_buffer);
signal.from_hz_to_hz(sinc, source_hz, target_hz)
}
fn main() -> anyhow::Result<()> {
let host = cpal::default_host();
let device = get_voicemeeter_input_device(&host);
let config = device.default_input_config()?;
let sample_rate = config.sample_rate().0 as f64;
let channels = config.channels() as usize;
let (tx, rx) = mpsc::channel::<Vec<f32>>();
let stream = device.build_input_stream(
&config.into(),
move |data: &[f32], _info: &cpal::InputCallbackInfo| {
let mono = data
.chunks(channels)
.map(|frame| frame.iter().sum::<f32>() / channels as f32);
tx.send(mono.collect()).unwrap();
},
|err| {
eprintln!("warning: input stream error: {}", err);
},
None,
)?;
stream.play()?;
let samples = rx.into_iter().flat_map(|x| x.into_iter());
let signal = signal::from_iter(samples);
let signal = sinc_resample(signal, sample_rate, 16000.0);
// now use the signal however you want (warning: next()/until_exhausted() will block until the stream is dropped)
// example: write to a wav file for 10 seconds
let processing_thread = thread::spawn(move || {
let mut writer = hound::WavWriter::create(
"output.wav",
hound::WavSpec {
channels: 1,
sample_rate: 16000,
bits_per_sample: 32,
sample_format: hound::SampleFormat::Float,
},
)
.unwrap();
for sample in signal.until_exhausted() {
writer.write_sample(sample).unwrap();
}
writer.finalize().unwrap();
});
thread::sleep(std::time::Duration::from_millis(10000));
drop(stream);
println!("dropped stream");
processing_thread.join().unwrap();
println!("finished processing");
Ok(())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment