Skip to content

Instantly share code, notes, and snippets.

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 {
.expect("Failed to get input devices")
.find(|device| {
.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(
move |data: &[f32], _info: &cpal::InputCallbackInfo| {
let mono = data
.map(|frame| frame.iter().sum::<f32>() / channels as f32);
|err| {
eprintln!("warning: input stream error: {}", err);
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(
hound::WavSpec {
channels: 1,
sample_rate: 16000,
bits_per_sample: 32,
sample_format: hound::SampleFormat::Float,
for sample in signal.until_exhausted() {
println!("dropped stream");
println!("finished processing");
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment